<commit_before>/**
 * Copyright (c) 2016 DeepCortex GmbH <legal@eventql.io>
 * Authors:
 *   - Laura Schlimmer <laura@eventql.io>
 *   - Paul Asmuth <paul@eventql.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/cli/commands/table_import.h>
#include <eventql/util/cli/flagparser.h>
#include "eventql/util/logging.h"
#include "eventql/transport/native/frames/insert.h"
#include "eventql/transport/native/frames/error.h"

namespace eventql {
namespace cli {

const String TableImport::kName_ = "table-import";
const String TableImport::kDescription_ = "Import json or csv data to a table.";

TableImport::TableImport(
    RefPtr<ProcessConfig> process_cfg) :
    process_cfg_(process_cfg),
    status_(ReturnCode::success()),
    complete_(false) {}

Status TableImport::execute(
    const std::vector<std::string>& argv,
    FileInputStream* stdin_is,
    OutputStream* stdout_os,
    OutputStream* stderr_os) {
  ::cli::FlagParser flags;

  flags.defineFlag(
      "database",
      ::cli::FlagParser::T_STRING,
      true,
      "d",
      NULL,
      "database",
      "<string>");

  flags.defineFlag(
      "table",
      ::cli::FlagParser::T_STRING,
      true,
      "t",
      NULL,
      "table name",
      "<string>");

  flags.defineFlag(
      "host",
      ::cli::FlagParser::T_STRING,
      true,
      "h",
      NULL,
      "host name",
      "<string>");

  flags.defineFlag(
      "port",
      ::cli::FlagParser::T_INTEGER,
      true,
      "p",
      NULL,
      "port",
      "<integer>");

  flags.defineFlag(
      "file",
      ::cli::FlagParser::T_STRING,
      true,
      "f",
      NULL,
      "file path",
      "<string>");

  flags.defineFlag(
      "connections",
      ::cli::FlagParser::T_INTEGER,
      false,
      "c",
      "10",
      "number of connections",
      "<num>");

  try {
    flags.parseArgv(argv);

  } catch (const Exception& e) {

    stderr_os->write(StringUtil::format(
        "$0: $1\n",
       e.getTypeName(),
        e.getMessage()));
    return Status(e);
  }

  database_ = flags.getString("database");
  table_ = flags.getString("table");
  host_ = flags.getString("host");
  port_ = flags.getInt("port");
  num_threads_ = flags.isSet("connections") ?
      flags.getInt("connections") :
      kDefaultNumThreads;
  threads_.resize(num_threads_);

  /* auth data */
  if (process_cfg_->hasProperty("client.user")) {
    auth_data_.emplace_back(
        "user",
        process_cfg_->getString("client.user").get());
  }

  if (process_cfg_->hasProperty("client.password")) {
    auth_data_.emplace_back(
        "password",
        process_cfg_->getString("client.").get());
  }

  if (process_cfg_->hasProperty("client.auth_token")) {
    auth_data_.emplace_back(
        "auth_token",
        process_cfg_->getString("client.auth_token").get());
  }

  return run(flags.getString("file"));
}

Status TableImport::run(const std::string& file) {
  std::unique_ptr<FileInputStream> is;
  try {
    is = FileInputStream::openFile(file);

  } catch (const Exception& e) {
    return Status(e);
  }

  /* start threads */
  for (size_t i = 0; i < num_threads_; ++i) {
    threads_[i] = std::thread(std::bind(&TableImport::runThread, this));
  }

  /* read and enqueue lines in batches */
  std::vector<std::string> batch;
  std::string line;
  uint64_t cur_size = 0;
  while (is->readLine(&line)) {
    if (cur_size + line.size() > kDefaultBatchSize) {
      if (!enqueueBatch(std::move(batch))) {
        goto exit;
      }

      batch.clear();
      cur_size = 0;
    }

    batch.emplace_back(line);
    cur_size += line.size();
    line.clear();
  }

  if (batch.size() > 0) {
    if (!enqueueBatch(std::move(batch))) {
      goto exit;
    }
  }

  /* send upload complete signal to trigger thread shutdown */
  {
    std::unique_lock<std::mutex> lk(mutex_);
    complete_ = true;
    cv_.notify_all();
  }

exit:

  /* join threads */
  for (auto& t : threads_) {
    if (t.joinable()) {
      t.join();
    }
  }

  return status_;
}

void TableImport::runThread() {
  try {
    native_transport::TCPClient client(nullptr, nullptr);
    /* connect to server */
    auto rc = client.connect(
        host_,
        port_,
        false,
        auth_data_);
    if (!rc.isSuccess()) {
      setError(rc);
      return;
    }

    UploadBatch batch;
    while (popBatch(&batch)) {
      auto rc = uploadBatch(&client, batch);
      if (!rc.isSuccess()) {
        setError(rc);
        client.close();
        return;
      }

      batch.clear();
    }

    client.close();

  } catch (const std::exception& e) {
    setError(e);
  }
}

static const size_t kMaxQueueSize = 32;

bool TableImport::enqueueBatch(UploadBatch&& batch) {
  std::unique_lock<std::mutex> lk(mutex_);

  for (;;) {
    if (!status_.isSuccess() || complete_) {
      return false;
    }

    if (queue_.size() < kMaxQueueSize) {
      break;
    }

    cv_.wait(lk);
  }

  queue_.push_back(std::move(batch));
  cv_.notify_all();
  return true;
}

bool TableImport::popBatch(UploadBatch* batch) {
  std::unique_lock<std::mutex> lk(mutex_);

  for (;;) {
    if (!status_.isSuccess()) {
      return false;
    }

    if (!queue_.empty()) {
      break;
    }

    if (complete_) {
      return false;
    } else {
      cv_.wait(lk);
    }
  }

  *batch = queue_.front();
  queue_.pop_front();
  cv_.notify_all();
  return true;
}

void TableImport::setError(const ReturnCode& err) {
  std::unique_lock<std::mutex> lk(mutex_);
  status_ = err;
  cv_.notify_all();
}

Status TableImport::uploadBatch(
    native_transport::TCPClient* client,
    const UploadBatch& batch) {
  /* insert */
  native_transport::InsertFrame i_frame;
  i_frame.setDatabase(database_);
  i_frame.setTable(table_);
  i_frame.setRecordEncoding(EVQL_INSERT_CTYPE_JSON);

  for (auto l : batch) {
    i_frame.addRecord(l);
  }

  auto rc = client->sendFrame(&i_frame, 0);
  if (!rc.isSuccess()) {
    return rc;
  }

  uint16_t ret_opcode = 0;
  uint16_t ret_flags;
  std::string ret_payload;
  while (ret_opcode != EVQL_OP_ACK) {
    auto rc = client->recvFrame(
        &ret_opcode,
        &ret_flags,
        &ret_payload,
        kMicrosPerSecond); // FIXME

    if (!rc.isSuccess()) {
      return rc;
    }

    switch (ret_opcode) {
      case EVQL_OP_ACK:
      case EVQL_OP_HEARTBEAT:
        continue;
      case EVQL_OP_ERROR: {
        native_transport::ErrorFrame eframe;
        eframe.parseFrom(ret_payload.data(), ret_payload.size());
        return ReturnCode::error("ERUNTIME", eframe.getError());
      }
      default:
        return ReturnCode::error("ERUNTIME", "unexpected opcode");
    }
  }

  return Status::success();
}

const String& TableImport::getName() const {
  return kName_;
}

const String& TableImport::getDescription() const {
  return kDescription_;
}

void TableImport::printHelp(OutputStream* stdout_os) const {
  stdout_os->write(StringUtil::format(
      "\nevqlctl-$0 - $1\n\n", kName_, kDescription_));

  stdout_os->write(
      "Usage: evqlctl table-import [OPTIONS]\n"
      "   -c, --connections <num>     Number of concurrent connections\n"
      "  -d, --database <db>          Select a database.\n"
      "  -t, --table <tbl>            Select a destination table.\n"
      "  -f, --file <file>            Set the path of the file to import.\n"
      "  -h, --host <hostname>        Set the EventQL hostname.\n"
      "  -p, --port <port>            Set the EventQL port.\n");
}

} // namespace cli
} // namespace eventql

<commit_msg>fixed set error from exception<commit_after>/**
 * Copyright (c) 2016 DeepCortex GmbH <legal@eventql.io>
 * Authors:
 *   - Laura Schlimmer <laura@eventql.io>
 *   - Paul Asmuth <paul@eventql.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/cli/commands/table_import.h>
#include <eventql/util/cli/flagparser.h>
#include "eventql/util/logging.h"
#include "eventql/transport/native/frames/insert.h"
#include "eventql/transport/native/frames/error.h"

namespace eventql {
namespace cli {

const String TableImport::kName_ = "table-import";
const String TableImport::kDescription_ = "Import json or csv data to a table.";

TableImport::TableImport(
    RefPtr<ProcessConfig> process_cfg) :
    process_cfg_(process_cfg),
    status_(ReturnCode::success()),
    complete_(false) {}

Status TableImport::execute(
    const std::vector<std::string>& argv,
    FileInputStream* stdin_is,
    OutputStream* stdout_os,
    OutputStream* stderr_os) {
  ::cli::FlagParser flags;

  flags.defineFlag(
      "database",
      ::cli::FlagParser::T_STRING,
      true,
      "d",
      NULL,
      "database",
      "<string>");

  flags.defineFlag(
      "table",
      ::cli::FlagParser::T_STRING,
      true,
      "t",
      NULL,
      "table name",
      "<string>");

  flags.defineFlag(
      "host",
      ::cli::FlagParser::T_STRING,
      true,
      "h",
      NULL,
      "host name",
      "<string>");

  flags.defineFlag(
      "port",
      ::cli::FlagParser::T_INTEGER,
      true,
      "p",
      NULL,
      "port",
      "<integer>");

  flags.defineFlag(
      "file",
      ::cli::FlagParser::T_STRING,
      true,
      "f",
      NULL,
      "file path",
      "<string>");

  flags.defineFlag(
      "connections",
      ::cli::FlagParser::T_INTEGER,
      false,
      "c",
      "10",
      "number of connections",
      "<num>");

  try {
    flags.parseArgv(argv);

  } catch (const Exception& e) {

    stderr_os->write(StringUtil::format(
        "$0: $1\n",
       e.getTypeName(),
        e.getMessage()));
    return Status(e);
  }

  database_ = flags.getString("database");
  table_ = flags.getString("table");
  host_ = flags.getString("host");
  port_ = flags.getInt("port");
  num_threads_ = flags.isSet("connections") ?
      flags.getInt("connections") :
      kDefaultNumThreads;
  threads_.resize(num_threads_);

  /* auth data */
  if (process_cfg_->hasProperty("client.user")) {
    auth_data_.emplace_back(
        "user",
        process_cfg_->getString("client.user").get());
  }

  if (process_cfg_->hasProperty("client.password")) {
    auth_data_.emplace_back(
        "password",
        process_cfg_->getString("client.").get());
  }

  if (process_cfg_->hasProperty("client.auth_token")) {
    auth_data_.emplace_back(
        "auth_token",
        process_cfg_->getString("client.auth_token").get());
  }

  return run(flags.getString("file"));
}

Status TableImport::run(const std::string& file) {
  std::unique_ptr<FileInputStream> is;
  try {
    is = FileInputStream::openFile(file);

  } catch (const Exception& e) {
    return Status(e);
  }

  /* start threads */
  for (size_t i = 0; i < num_threads_; ++i) {
    threads_[i] = std::thread(std::bind(&TableImport::runThread, this));
  }

  /* read and enqueue lines in batches */
  std::vector<std::string> batch;
  std::string line;
  uint64_t cur_size = 0;
  while (is->readLine(&line)) {
    if (cur_size + line.size() > kDefaultBatchSize) {
      if (!enqueueBatch(std::move(batch))) {
        goto exit;
      }

      batch.clear();
      cur_size = 0;
    }

    batch.emplace_back(line);
    cur_size += line.size();
    line.clear();
  }

  if (batch.size() > 0) {
    if (!enqueueBatch(std::move(batch))) {
      goto exit;
    }
  }

  /* send upload complete signal to trigger thread shutdown */
  {
    std::unique_lock<std::mutex> lk(mutex_);
    complete_ = true;
    cv_.notify_all();
  }

exit:

  /* join threads */
  for (auto& t : threads_) {
    if (t.joinable()) {
      t.join();
    }
  }

  return status_;
}

void TableImport::runThread() {
  try {
    native_transport::TCPClient client(nullptr, nullptr);
    /* connect to server */
    auto rc = client.connect(
        host_,
        port_,
        false,
        auth_data_);
    if (!rc.isSuccess()) {
      setError(rc);
      return;
    }

    UploadBatch batch;
    while (popBatch(&batch)) {
      auto rc = uploadBatch(&client, batch);
      if (!rc.isSuccess()) {
        setError(rc);
        client.close();
        return;
      }

      batch.clear();
    }

    client.close();

  } catch (const std::exception& e) {
    setError(ReturnCode::exception(e));
  }
}

static const size_t kMaxQueueSize = 32;

bool TableImport::enqueueBatch(UploadBatch&& batch) {
  std::unique_lock<std::mutex> lk(mutex_);

  for (;;) {
    if (!status_.isSuccess() || complete_) {
      return false;
    }

    if (queue_.size() < kMaxQueueSize) {
      break;
    }

    cv_.wait(lk);
  }

  queue_.push_back(std::move(batch));
  cv_.notify_all();
  return true;
}

bool TableImport::popBatch(UploadBatch* batch) {
  std::unique_lock<std::mutex> lk(mutex_);

  for (;;) {
    if (!status_.isSuccess()) {
      return false;
    }

    if (!queue_.empty()) {
      break;
    }

    if (complete_) {
      return false;
    } else {
      cv_.wait(lk);
    }
  }

  *batch = queue_.front();
  queue_.pop_front();
  cv_.notify_all();
  return true;
}

void TableImport::setError(const ReturnCode& err) {
  std::unique_lock<std::mutex> lk(mutex_);
  status_ = err;
  cv_.notify_all();
}

Status TableImport::uploadBatch(
    native_transport::TCPClient* client,
    const UploadBatch& batch) {
  /* insert */
  native_transport::InsertFrame i_frame;
  i_frame.setDatabase(database_);
  i_frame.setTable(table_);
  i_frame.setRecordEncoding(EVQL_INSERT_CTYPE_JSON);

  for (auto l : batch) {
    i_frame.addRecord(l);
  }

  auto rc = client->sendFrame(&i_frame, 0);
  if (!rc.isSuccess()) {
    return rc;
  }

  uint16_t ret_opcode = 0;
  uint16_t ret_flags;
  std::string ret_payload;
  while (ret_opcode != EVQL_OP_ACK) {
    auto rc = client->recvFrame(
        &ret_opcode,
        &ret_flags,
        &ret_payload,
        kMicrosPerSecond); // FIXME

    if (!rc.isSuccess()) {
      return rc;
    }

    switch (ret_opcode) {
      case EVQL_OP_ACK:
      case EVQL_OP_HEARTBEAT:
        continue;
      case EVQL_OP_ERROR: {
        native_transport::ErrorFrame eframe;
        eframe.parseFrom(ret_payload.data(), ret_payload.size());
        return ReturnCode::error("ERUNTIME", eframe.getError());
      }
      default:
        return ReturnCode::error("ERUNTIME", "unexpected opcode");
    }
  }

  return Status::success();
}

const String& TableImport::getName() const {
  return kName_;
}

const String& TableImport::getDescription() const {
  return kDescription_;
}

void TableImport::printHelp(OutputStream* stdout_os) const {
  stdout_os->write(StringUtil::format(
      "\nevqlctl-$0 - $1\n\n", kName_, kDescription_));

  stdout_os->write(
      "Usage: evqlctl table-import [OPTIONS]\n"
      "   -c, --connections <num>     Number of concurrent connections\n"
      "  -d, --database <db>          Select a database.\n"
      "  -t, --table <tbl>            Select a destination table.\n"
      "  -f, --file <file>            Set the path of the file to import.\n"
      "  -h, --host <hostname>        Set the EventQL hostname.\n"
      "  -p, --port <port>            Set the EventQL port.\n");
}

} // namespace cli
} // namespace eventql

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: StartMarker.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: ihi $ $Date: 2007-11-20 19:12:53 $
 *
 *  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
 *
 ************************************************************************/
#include "precompiled_reportdesign.hxx"

#ifndef RPTUI_STARTMARKER_HXX
#include "StartMarker.hxx"
#endif
#ifndef _SV_IMAGE_HXX
#include <vcl/image.hxx>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif
#ifndef _RPTUI_DLGRESID_HRC
#include "RptResId.hrc"
#endif
#ifndef _RPTUI_MODULE_HELPER_RPT_HXX_
#include "ModuleHelper.hxx"
#endif
#ifndef RPTUI_COLORCHANGER_HXX
#include "ColorChanger.hxx"
#endif
#ifndef RPTUI_REPORT_DEFINES_HXX
#include "ReportDefines.hxx"
#endif
#ifndef RPTUI_SECTIONSWINDOW_HXX
#include "SectionsWindow.hxx"
#endif
#ifndef RTPUI_REPORTDESIGN_HELPID_HRC
#include "helpids.hrc"
#endif
#ifndef _SV_HELP_HXX
#include <vcl/help.hxx>
#endif
#include <vcl/gradient.hxx>
#include <vcl/lineinfo.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <svtools/syslocale.hxx>
#ifndef _SFXSMPLHINT_HXX
#include <svtools/smplhint.hxx>
#endif

#define CORNER_SPACE     5
#define TEXT_WIDTH      10
#define STRT_BORDER      6

//=====================================================================
namespace rptui
{
//=====================================================================

Image*  OStartMarker::s_pDefCollapsed       = NULL;
Image*  OStartMarker::s_pDefExpanded        = NULL;
Image*  OStartMarker::s_pDefCollapsedHC = NULL;
Image*  OStartMarker::s_pDefExpandedHC  = NULL;
oslInterlockedCount OStartMarker::s_nImageRefCount  = 0;

DBG_NAME( rpt_OStartMarker )
// -----------------------------------------------------------------------------
OStartMarker::OStartMarker(OSectionsWindow* _pParent,const ::rtl::OUString& _sColorEntry)
: OColorListener(_pParent,_sColorEntry)
,m_aVRuler(this,WB_VERT)
,m_aText(this,WB_WORDBREAK)
,m_aImage(this,WB_LEFT|WB_TOP)
,m_pParent(_pParent)
,m_nCornerSize(CORNER_SPACE)
,m_bShowRuler(sal_True)
{
    DBG_CTOR( rpt_OStartMarker,NULL);
    SetUniqueId(HID_STARTMARKER);
    osl_incrementInterlockedCount(&s_nImageRefCount);
    initDefaultNodeImages();
    ImplInitSettings();
    m_aText.SetHelpId(HID_START_TITLE);
    m_aImage.SetHelpId(HID_START_IMAGE);
    m_aText.Show();
    m_aImage.Show();
    m_aVRuler.Show();
    m_aVRuler.Activate();
    m_aVRuler.SetPagePos(0);
    m_aVRuler.SetBorders();
    m_aVRuler.SetIndents();
    m_aVRuler.SetMargin1();
    m_aVRuler.SetMargin2();
    const MeasurementSystem eSystem = SvtSysLocale().GetLocaleData().getMeasurementSystemEnum();
    m_aVRuler.SetUnit(MEASURE_METRIC == eSystem ? FUNIT_CM : FUNIT_INCH);

}
// -----------------------------------------------------------------------------
OStartMarker::~OStartMarker()
{
    DBG_DTOR( rpt_OStartMarker,NULL);
    if ( osl_decrementInterlockedCount(&s_nImageRefCount) == 0 )
    {
        DELETEZ(s_pDefCollapsed);
        DELETEZ(s_pDefExpanded);
        DELETEZ(s_pDefCollapsedHC);
        DELETEZ(s_pDefExpandedHC);
    } // if ( osl_decrementInterlockedCount(&s_nImageRefCount) == 0 )
}
// -----------------------------------------------------------------------------
sal_Int32 OStartMarker::getWidth() const
{
    return (GetDisplayBackground().GetColor().IsDark() ? s_pDefExpandedHC : s_pDefCollapsed)->GetSizePixel().Width() + GetTextWidth(m_aText.GetText(),0,::std::min<USHORT>(TEXT_WIDTH,m_aText.GetText().Len())) + 2*REPORT_EXTRA_SPACE;
}
// -----------------------------------------------------------------------------
sal_Int32 OStartMarker::getMinHeight() const
{
    return m_aText.GetTextHeight() + 2*STRT_BORDER + 2;
}
// -----------------------------------------------------------------------------
void OStartMarker::Paint( const Rectangle& rRect )
{
    Window::Paint( rRect );
    //SetUpdateMode(FALSE);
    Size aSize = GetSizePixel();
    long nSize = aSize.Width();
    if ( !isCollapsed() )
        nSize = aSize.Width() - m_aVRuler.GetSizePixel().Width() - m_nCornerSize;
    SetClipRegion(Region(Rectangle(Point(),Size( nSize,aSize.Height()))));
    aSize.Width() += m_nCornerSize;

    Point aGcc3WorkaroundTemporary;
    Rectangle aWholeRect(aGcc3WorkaroundTemporary,aSize);
    {
        const ColorChanger aColors( this, m_nTextBoundaries, m_nColor );

        //aGradient.SetBorder(static_cast<USHORT>(m_nCornerSize));
        PolyPolygon aPoly;
        aPoly.Insert(Polygon(aWholeRect,m_nCornerSize,m_nCornerSize));

        Color aStartColor(m_nColor);
        aStartColor.IncreaseLuminance(10);
        USHORT nHue = 0;
        USHORT nSat = 0;
        USHORT nBri = 0;
        aStartColor.RGBtoHSB(nHue, nSat, nBri);
        nSat += 40;
        Color aEndColor(Color::HSBtoRGB(nHue, nSat, nBri));
        Gradient aGradient(GRADIENT_LINEAR,aStartColor,aEndColor);
        aGradient.SetSteps(static_cast<USHORT>(aSize.Height()));

        DrawGradient(aPoly ,aGradient);
    }
    if ( m_bMarked )
    {
#define DIFF_DIST    2
        Rectangle aRect( Point(m_nCornerSize,m_nCornerSize),
                         Size(aSize.Width() - m_nCornerSize- m_nCornerSize,aSize.Height() - m_nCornerSize- m_nCornerSize));
        ColorChanger aColors( this, COL_WHITE, COL_WHITE );
        DrawPolyLine(Polygon(aRect),LineInfo(LINE_SOLID,2));
    }
}
// -----------------------------------------------------------------------------
void OStartMarker::setColor()
{
    const Color aColor(m_nColor);
    Color aTextColor = GetTextColor();
    if ( aColor.GetLuminance() < 128 )
        aTextColor = COL_WHITE;
    m_aText.SetTextColor(aTextColor);
    m_aText.SetLineColor(m_nColor);
}
// -----------------------------------------------------------------------
void OStartMarker::MouseButtonUp( const MouseEvent& rMEvt )
{
    if ( !rMEvt.IsLeft() )
        return;

    Point aPos( rMEvt.GetPosPixel());

    const Size aOutputSize = GetOutputSizePixel();
    if( aPos.X() > aOutputSize.Width() || aPos.Y() > aOutputSize.Height() )
        return;
    Rectangle aRect(m_aImage.GetPosPixel(),m_aImage.GetImage().GetSizePixel());
    if ( rMEvt.GetClicks() == 2 || aRect.IsInside( aPos ) )
    {
        m_bCollapsed = !m_bCollapsed;

        Image* pImage = NULL;
        if ( GetDisplayBackground().GetColor().IsDark() )
            pImage = m_bCollapsed ? s_pDefCollapsedHC : s_pDefExpandedHC;
        else
            pImage = m_bCollapsed ? s_pDefCollapsed : s_pDefExpanded;
        m_aImage.SetImage(*pImage);

        m_aVRuler.Show(!m_bCollapsed && m_bShowRuler);
        m_nCornerSize = CORNER_SPACE;
        if ( m_aCollapsedLink.IsSet() )
            m_aCollapsedLink.Call(this);
    }

    m_pParent->showProperties(this);
}
// -----------------------------------------------------------------------
void OStartMarker::initDefaultNodeImages()
{
    if ( !s_pDefCollapsed )
    {
        s_pDefCollapsed = new Image( ModuleRes( RID_IMG_TREENODE_COLLAPSED) );
        s_pDefCollapsedHC = new Image( ModuleRes( RID_IMG_TREENODE_COLLAPSED_HC ) );
        s_pDefExpanded = new Image( ModuleRes( RID_IMG_TREENODE_EXPANDED ) );
        s_pDefExpandedHC = new Image( ModuleRes( RID_IMG_TREENODE_EXPANDED_HC ) );
    }

    Image* pImage = NULL;
    if ( GetDisplayBackground().GetColor().IsDark() )
    {
        pImage = m_bCollapsed ? s_pDefCollapsedHC : s_pDefExpandedHC;
    }
    else
    {
        pImage = m_bCollapsed ? s_pDefCollapsed : s_pDefExpanded;
    }
    m_aImage.SetImage(*pImage);
    m_aImage.SetMouseTransparent(TRUE);
    m_aImage.SetBackground();
    m_aText.SetBackground();
    m_aText.SetMouseTransparent(TRUE);
}
// -----------------------------------------------------------------------
void OStartMarker::ImplInitSettings()
{
    SetBackground( Wallpaper( Application::GetSettings().GetStyleSettings().GetDialogColor() ) );
    SetFillColor( Application::GetSettings().GetStyleSettings().GetDialogColor() );
    //SetTextFillColor( Application::GetSettings().GetStyleSettings().GetDarkShadowColor() );
    setColor();
}
//------------------------------------------------------------------------------
void OStartMarker::Resize()
{
    const Size aOutputSize( GetOutputSize() );
    const long nOutputWidth = aOutputSize.Width();
    const long nOutputHeight    = aOutputSize.Height();

    const Size aImageSize = m_aImage.GetImage().GetSizePixel();
    sal_Int32 nY = ::std::min<sal_Int32>(static_cast<sal_Int32>(REPORT_EXTRA_SPACE),static_cast<sal_Int32>((nOutputHeight - aImageSize.Height()) * 0.5));
    if ( m_bCollapsed )
        nY = static_cast<sal_Int32>((nOutputHeight - aImageSize.Height()) * 0.5);
    Point aPos(REPORT_EXTRA_SPACE,nY);

    m_aImage.SetPosSizePixel(aPos,Size(aImageSize.Width() + REPORT_EXTRA_SPACE,nOutputHeight - 2*nY));
    aPos.X() += aImageSize.Width() + REPORT_EXTRA_SPACE;
    aPos.Y() -= 2;

    const long nVRulerWidth = m_aVRuler.GetSizePixel().Width();
    const Point aRulerPos(nOutputWidth - nVRulerWidth - 5,0);

    m_aText.SetPosSizePixel(aPos,Size(aRulerPos.X() - aPos.X(),nOutputHeight - 2*aPos.Y()));

    m_aVRuler.SetPosSizePixel(aRulerPos,Size(nVRulerWidth,nOutputHeight));
}
// -----------------------------------------------------------------------------
void OStartMarker::setTitle(const String& _sTitle)
{
    m_aText.SetText(_sTitle);
}
// -----------------------------------------------------------------------------
void OStartMarker::Notify(SfxBroadcaster & rBc, SfxHint const & rHint)
{
    OColorListener::Notify(rBc, rHint);
    if (rHint.ISA(SfxSimpleHint)
        && (static_cast< SfxSimpleHint const & >(rHint).GetId()
            == SFX_HINT_COLORS_CHANGED))
    {
        setColor();
        //m_aText.Invalidate();
        Invalidate(INVALIDATE_CHILDREN);
    }
}
//----------------------------------------------------------------------------
void OStartMarker::showRuler(sal_Bool _bShow)
{
    m_bShowRuler = _bShow;
    m_aVRuler.Show(!m_bCollapsed && m_bShowRuler);
}
//------------------------------------------------------------------------------
sal_Int32 OStartMarker::getRulerOffset() const
{
    return m_aVRuler.GetSizePixel().Width();
}

//------------------------------------------------------------------------------
void OStartMarker::RequestHelp( const HelpEvent& rHEvt )
{
    if( m_aText.GetText().Len())
    {
        // Hilfe anzeigen
        Rectangle aItemRect(rHEvt.GetMousePosPixel(),Size(GetSizePixel().Width(),getMinHeight()));
        //aItemRect = LogicToPixel( aItemRect );
        Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
        aItemRect.Left()   = aPt.X();
        aItemRect.Top()    = aPt.Y();
        aPt = OutputToScreenPixel( aItemRect.BottomRight() );
        aItemRect.Right()  = aPt.X();
        aItemRect.Bottom() = aPt.Y();
        if( rHEvt.GetMode() == HELPMODE_BALLOON )
            Help::ShowBalloon( this, aItemRect.Center(), aItemRect, m_aText.GetText());
        else
            Help::ShowQuickHelp( this, aItemRect, m_aText.GetText() );
    }
}
// -----------------------------------------------------------------------------
void OStartMarker::setCollapsed(sal_Bool _bCollapsed)
{
    OColorListener::setCollapsed(_bCollapsed);
    showRuler(_bCollapsed);
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// =======================================================================
}
// =======================================================================
<commit_msg>INTEGRATION: CWS reportdesign02 (1.4.18); FILE MERGED 2008/01/15 11:50:21 oj 1.4.18.1: change helpid names<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: StartMarker.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2008-01-29 13:52:06 $
 *
 *  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
 *
 ************************************************************************/
#include "precompiled_reportdesign.hxx"

#ifndef RPTUI_STARTMARKER_HXX
#include "StartMarker.hxx"
#endif
#ifndef _SV_IMAGE_HXX
#include <vcl/image.hxx>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif
#ifndef _RPTUI_DLGRESID_HRC
#include "RptResId.hrc"
#endif
#ifndef _RPTUI_MODULE_HELPER_RPT_HXX_
#include "ModuleHelper.hxx"
#endif
#ifndef RPTUI_COLORCHANGER_HXX
#include "ColorChanger.hxx"
#endif
#ifndef RPTUI_REPORT_DEFINES_HXX
#include "ReportDefines.hxx"
#endif
#ifndef RPTUI_SECTIONSWINDOW_HXX
#include "SectionsWindow.hxx"
#endif
#ifndef RTPUI_REPORTDESIGN_HELPID_HRC
#include "helpids.hrc"
#endif
#ifndef _SV_HELP_HXX
#include <vcl/help.hxx>
#endif
#include <vcl/gradient.hxx>
#include <vcl/lineinfo.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <svtools/syslocale.hxx>
#ifndef _SFXSMPLHINT_HXX
#include <svtools/smplhint.hxx>
#endif

#define CORNER_SPACE     5
#define TEXT_WIDTH      10
#define STRT_BORDER      6

//=====================================================================
namespace rptui
{
//=====================================================================

Image*  OStartMarker::s_pDefCollapsed       = NULL;
Image*  OStartMarker::s_pDefExpanded        = NULL;
Image*  OStartMarker::s_pDefCollapsedHC = NULL;
Image*  OStartMarker::s_pDefExpandedHC  = NULL;
oslInterlockedCount OStartMarker::s_nImageRefCount  = 0;

DBG_NAME( rpt_OStartMarker )
// -----------------------------------------------------------------------------
OStartMarker::OStartMarker(OSectionsWindow* _pParent,const ::rtl::OUString& _sColorEntry)
: OColorListener(_pParent,_sColorEntry)
,m_aVRuler(this,WB_VERT)
,m_aText(this,WB_WORDBREAK)
,m_aImage(this,WB_LEFT|WB_TOP)
,m_pParent(_pParent)
,m_nCornerSize(CORNER_SPACE)
,m_bShowRuler(sal_True)
{
    DBG_CTOR( rpt_OStartMarker,NULL);
    SetUniqueId(HID_RPT_STARTMARKER);
    osl_incrementInterlockedCount(&s_nImageRefCount);
    initDefaultNodeImages();
    ImplInitSettings();
    m_aText.SetHelpId(HID_RPT_START_TITLE);
    m_aImage.SetHelpId(HID_RPT_START_IMAGE);
    m_aText.Show();
    m_aImage.Show();
    m_aVRuler.Show();
    m_aVRuler.Activate();
    m_aVRuler.SetPagePos(0);
    m_aVRuler.SetBorders();
    m_aVRuler.SetIndents();
    m_aVRuler.SetMargin1();
    m_aVRuler.SetMargin2();
    const MeasurementSystem eSystem = SvtSysLocale().GetLocaleData().getMeasurementSystemEnum();
    m_aVRuler.SetUnit(MEASURE_METRIC == eSystem ? FUNIT_CM : FUNIT_INCH);

}
// -----------------------------------------------------------------------------
OStartMarker::~OStartMarker()
{
    DBG_DTOR( rpt_OStartMarker,NULL);
    if ( osl_decrementInterlockedCount(&s_nImageRefCount) == 0 )
    {
        DELETEZ(s_pDefCollapsed);
        DELETEZ(s_pDefExpanded);
        DELETEZ(s_pDefCollapsedHC);
        DELETEZ(s_pDefExpandedHC);
    } // if ( osl_decrementInterlockedCount(&s_nImageRefCount) == 0 )
}
// -----------------------------------------------------------------------------
sal_Int32 OStartMarker::getWidth() const
{
    return (GetDisplayBackground().GetColor().IsDark() ? s_pDefExpandedHC : s_pDefCollapsed)->GetSizePixel().Width() + GetTextWidth(m_aText.GetText(),0,::std::min<USHORT>(TEXT_WIDTH,m_aText.GetText().Len())) + 2*REPORT_EXTRA_SPACE;
}
// -----------------------------------------------------------------------------
sal_Int32 OStartMarker::getMinHeight() const
{
    return m_aText.GetTextHeight() + 2*STRT_BORDER + 2;
}
// -----------------------------------------------------------------------------
void OStartMarker::Paint( const Rectangle& rRect )
{
    Window::Paint( rRect );
    //SetUpdateMode(FALSE);
    Size aSize = GetSizePixel();
    long nSize = aSize.Width();
    if ( !isCollapsed() )
        nSize = aSize.Width() - m_aVRuler.GetSizePixel().Width() - m_nCornerSize;
    SetClipRegion(Region(Rectangle(Point(),Size( nSize,aSize.Height()))));
    aSize.Width() += m_nCornerSize;

    Point aGcc3WorkaroundTemporary;
    Rectangle aWholeRect(aGcc3WorkaroundTemporary,aSize);
    {
        const ColorChanger aColors( this, m_nTextBoundaries, m_nColor );

        //aGradient.SetBorder(static_cast<USHORT>(m_nCornerSize));
        PolyPolygon aPoly;
        aPoly.Insert(Polygon(aWholeRect,m_nCornerSize,m_nCornerSize));

        Color aStartColor(m_nColor);
        aStartColor.IncreaseLuminance(10);
        USHORT nHue = 0;
        USHORT nSat = 0;
        USHORT nBri = 0;
        aStartColor.RGBtoHSB(nHue, nSat, nBri);
        nSat += 40;
        Color aEndColor(Color::HSBtoRGB(nHue, nSat, nBri));
        Gradient aGradient(GRADIENT_LINEAR,aStartColor,aEndColor);
        aGradient.SetSteps(static_cast<USHORT>(aSize.Height()));

        DrawGradient(aPoly ,aGradient);
    }
    if ( m_bMarked )
    {
#define DIFF_DIST    2
        Rectangle aRect( Point(m_nCornerSize,m_nCornerSize),
                         Size(aSize.Width() - m_nCornerSize- m_nCornerSize,aSize.Height() - m_nCornerSize- m_nCornerSize));
        ColorChanger aColors( this, COL_WHITE, COL_WHITE );
        DrawPolyLine(Polygon(aRect),LineInfo(LINE_SOLID,2));
    }
}
// -----------------------------------------------------------------------------
void OStartMarker::setColor()
{
    const Color aColor(m_nColor);
    Color aTextColor = GetTextColor();
    if ( aColor.GetLuminance() < 128 )
        aTextColor = COL_WHITE;
    m_aText.SetTextColor(aTextColor);
    m_aText.SetLineColor(m_nColor);
}
// -----------------------------------------------------------------------
void OStartMarker::MouseButtonUp( const MouseEvent& rMEvt )
{
    if ( !rMEvt.IsLeft() )
        return;

    Point aPos( rMEvt.GetPosPixel());

    const Size aOutputSize = GetOutputSizePixel();
    if( aPos.X() > aOutputSize.Width() || aPos.Y() > aOutputSize.Height() )
        return;
    Rectangle aRect(m_aImage.GetPosPixel(),m_aImage.GetImage().GetSizePixel());
    if ( rMEvt.GetClicks() == 2 || aRect.IsInside( aPos ) )
    {
        m_bCollapsed = !m_bCollapsed;

        Image* pImage = NULL;
        if ( GetDisplayBackground().GetColor().IsDark() )
            pImage = m_bCollapsed ? s_pDefCollapsedHC : s_pDefExpandedHC;
        else
            pImage = m_bCollapsed ? s_pDefCollapsed : s_pDefExpanded;
        m_aImage.SetImage(*pImage);

        m_aVRuler.Show(!m_bCollapsed && m_bShowRuler);
        m_nCornerSize = CORNER_SPACE;
        if ( m_aCollapsedLink.IsSet() )
            m_aCollapsedLink.Call(this);
    }

    m_pParent->showProperties(this);
}
// -----------------------------------------------------------------------
void OStartMarker::initDefaultNodeImages()
{
    if ( !s_pDefCollapsed )
    {
        s_pDefCollapsed = new Image( ModuleRes( RID_IMG_TREENODE_COLLAPSED) );
        s_pDefCollapsedHC = new Image( ModuleRes( RID_IMG_TREENODE_COLLAPSED_HC ) );
        s_pDefExpanded = new Image( ModuleRes( RID_IMG_TREENODE_EXPANDED ) );
        s_pDefExpandedHC = new Image( ModuleRes( RID_IMG_TREENODE_EXPANDED_HC ) );
    }

    Image* pImage = NULL;
    if ( GetDisplayBackground().GetColor().IsDark() )
    {
        pImage = m_bCollapsed ? s_pDefCollapsedHC : s_pDefExpandedHC;
    }
    else
    {
        pImage = m_bCollapsed ? s_pDefCollapsed : s_pDefExpanded;
    }
    m_aImage.SetImage(*pImage);
    m_aImage.SetMouseTransparent(TRUE);
    m_aImage.SetBackground();
    m_aText.SetBackground();
    m_aText.SetMouseTransparent(TRUE);
}
// -----------------------------------------------------------------------
void OStartMarker::ImplInitSettings()
{
    SetBackground( Wallpaper( Application::GetSettings().GetStyleSettings().GetDialogColor() ) );
    SetFillColor( Application::GetSettings().GetStyleSettings().GetDialogColor() );
    //SetTextFillColor( Application::GetSettings().GetStyleSettings().GetDarkShadowColor() );
    setColor();
}
//------------------------------------------------------------------------------
void OStartMarker::Resize()
{
    const Size aOutputSize( GetOutputSize() );
    const long nOutputWidth = aOutputSize.Width();
    const long nOutputHeight    = aOutputSize.Height();

    const Size aImageSize = m_aImage.GetImage().GetSizePixel();
    sal_Int32 nY = ::std::min<sal_Int32>(static_cast<sal_Int32>(REPORT_EXTRA_SPACE),static_cast<sal_Int32>((nOutputHeight - aImageSize.Height()) * 0.5));
    if ( m_bCollapsed )
        nY = static_cast<sal_Int32>((nOutputHeight - aImageSize.Height()) * 0.5);
    Point aPos(REPORT_EXTRA_SPACE,nY);

    m_aImage.SetPosSizePixel(aPos,Size(aImageSize.Width() + REPORT_EXTRA_SPACE,nOutputHeight - 2*nY));
    aPos.X() += aImageSize.Width() + REPORT_EXTRA_SPACE;
    aPos.Y() -= 2;

    const long nVRulerWidth = m_aVRuler.GetSizePixel().Width();
    const Point aRulerPos(nOutputWidth - nVRulerWidth - 5,0);

    m_aText.SetPosSizePixel(aPos,Size(aRulerPos.X() - aPos.X(),nOutputHeight - 2*aPos.Y()));

    m_aVRuler.SetPosSizePixel(aRulerPos,Size(nVRulerWidth,nOutputHeight));
}
// -----------------------------------------------------------------------------
void OStartMarker::setTitle(const String& _sTitle)
{
    m_aText.SetText(_sTitle);
}
// -----------------------------------------------------------------------------
void OStartMarker::Notify(SfxBroadcaster & rBc, SfxHint const & rHint)
{
    OColorListener::Notify(rBc, rHint);
    if (rHint.ISA(SfxSimpleHint)
        && (static_cast< SfxSimpleHint const & >(rHint).GetId()
            == SFX_HINT_COLORS_CHANGED))
    {
        setColor();
        //m_aText.Invalidate();
        Invalidate(INVALIDATE_CHILDREN);
    }
}
//----------------------------------------------------------------------------
void OStartMarker::showRuler(sal_Bool _bShow)
{
    m_bShowRuler = _bShow;
    m_aVRuler.Show(!m_bCollapsed && m_bShowRuler);
}
//------------------------------------------------------------------------------
sal_Int32 OStartMarker::getRulerOffset() const
{
    return m_aVRuler.GetSizePixel().Width();
}

//------------------------------------------------------------------------------
void OStartMarker::RequestHelp( const HelpEvent& rHEvt )
{
    if( m_aText.GetText().Len())
    {
        // Hilfe anzeigen
        Rectangle aItemRect(rHEvt.GetMousePosPixel(),Size(GetSizePixel().Width(),getMinHeight()));
        //aItemRect = LogicToPixel( aItemRect );
        Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
        aItemRect.Left()   = aPt.X();
        aItemRect.Top()    = aPt.Y();
        aPt = OutputToScreenPixel( aItemRect.BottomRight() );
        aItemRect.Right()  = aPt.X();
        aItemRect.Bottom() = aPt.Y();
        if( rHEvt.GetMode() == HELPMODE_BALLOON )
            Help::ShowBalloon( this, aItemRect.Center(), aItemRect, m_aText.GetText());
        else
            Help::ShowQuickHelp( this, aItemRect, m_aText.GetText() );
    }
}
// -----------------------------------------------------------------------------
void OStartMarker::setCollapsed(sal_Bool _bCollapsed)
{
    OColorListener::setCollapsed(_bCollapsed);
    showRuler(_bCollapsed);
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// =======================================================================
}
// =======================================================================
<|endoftext|>


<commit_before>#include "src/block_sync.h"

#include <vector>

#include "src/util.h"

namespace redsea {

namespace {

const unsigned kBitmask16 = 0x000FFFF;
const unsigned kBitmask26 = 0x3FFFFFF;
const unsigned kBitmask28 = 0xFFFFFFF;

// "...the error-correction system should be enabled, but should be restricted
// by attempting to correct bursts of errors spanning one or two bits."
// Kopitz & Marks 1999: "RDS: The Radio Data System", p. 224
const unsigned kMaxErrorLength = 2;

const std::vector<uint16_t> offset_words =
  {0x0FC, 0x198, 0x168, 0x350, 0x1B4};
const std::map<uint16_t,eOffset> offset_syndromes =
  {{0x3D8,OFFSET_A},  {0x3D4,OFFSET_B}, {0x25C,OFFSET_C},
   {0x3CC,OFFSET_CI}, {0x258,OFFSET_D}};
const std::vector<uint16_t> block_number_for_offset =
  {0, 1, 2, 2, 3};

// Section B.1.1: '-- calculated by the modulo-two addition of all the rows of
// the -- matrix for which the corresponding coefficient in the -- vector is 1.'
uint32_t matrixMultiply(uint32_t vec, const std::vector<uint32_t>& matrix) {

  uint32_t result = 0;

  for (size_t k=0; k < matrix.size(); k++)
    if ((vec >> k) & 0x01)
      result ^= matrix[matrix.size() - 1 - k];

  return result;
}

// Section B.2.1: 'The calculation of the syndromes -- can easily be done by
// multiplying each word with the parity matrix H.'
uint32_t calcSyndrome(uint32_t vec) {

  static const std::vector<uint32_t> parity_check_matrix({
      0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004,
      0x002, 0x001, 0x2dc, 0x16e, 0x0b7, 0x287, 0x39f, 0x313,
      0x355, 0x376, 0x1bb, 0x201, 0x3dc,
      0x1ee, 0x0f7, 0x2a7, 0x38f, 0x31b
  });

  return matrixMultiply(vec, parity_check_matrix);
}

eOffset nextOffsetFor(eOffset o) {
  static const std::map<eOffset,eOffset> next_offset({
      {OFFSET_A, OFFSET_B}, {OFFSET_B, OFFSET_C},
      {OFFSET_C, OFFSET_D}, {OFFSET_CI, OFFSET_D},
      {OFFSET_D, OFFSET_A}
  });
  return next_offset.at(o);
}

// Precompute mapping of syndromes to error vectors
std::map<uint16_t,uint32_t> makeErrorLookupTable() {

  std::map<uint16_t,uint32_t> result;

  for (uint32_t e=1; e < (1 << kMaxErrorLength); e++) {
    for (unsigned shift=0; shift < 26; shift++) {
      uint32_t errvec = ((e << shift) & kBitmask26);

      uint32_t sy = calcSyndrome(errvec);
      result[sy] = errvec;
    }
  }
  return result;
}

} // namespace

BlockStream::BlockStream(eInputType input_type, bool has_echo) : bitcount_(0),
  prevbitcount_(0), left_to_read_(0), wideblock_(0), prevsync_(0),
  block_counter_(0), expected_offset_(OFFSET_A),
  received_offset_(OFFSET_INVALID), pi_(0), is_in_sync_(false),
  block_has_errors_(50),
#ifdef HAVE_LIQUID
  subcarrier_(has_echo),
#endif
  ascii_bits_(has_echo),
  error_lookup_(makeErrorLookupTable()),
  input_type_(input_type), is_eof_(false) {

}

int BlockStream::getNextBit() {
  int result = 0;
#ifdef HAVE_LIQUID
  if (input_type_ == INPUT_MPX) {
    result = subcarrier_.getNextBit();
    is_eof_ = subcarrier_.isEOF();
  }
#endif
  if (input_type_ == INPUT_ASCIIBITS) {
    result = ascii_bits_.getNextBit();
    is_eof_ = ascii_bits_.isEOF();
  }

  return result;
}

// Section B.2.2
uint32_t BlockStream::correctBurstErrors(uint32_t block) const {

  uint16_t synd_reg =
    calcSyndrome(block ^ offset_words[expected_offset_]);

  uint32_t corrected_block = block;

  if (error_lookup_.find(synd_reg) != error_lookup_.end()) {
    corrected_block = (block ^ offset_words[expected_offset_])
      ^ (error_lookup_.at(synd_reg));
  }

  return corrected_block;

}

// A block can't be decoded
void BlockStream::uncorrectable() {

  block_has_errors_[block_counter_ % block_has_errors_.size()] = true;

  unsigned num_erroneous_blocks = 0;
  for (bool e : block_has_errors_) {
    if (e)
      num_erroneous_blocks++;
  }

  // Sync is lost when >45 out of last 50 blocks are erroneous (Section C.1.2)
  if (is_in_sync_ && num_erroneous_blocks > 45) {
    is_in_sync_ = false;
    for (size_t i=0; i < block_has_errors_.size(); i++)
      block_has_errors_[i] = false;
    pi_ = 0x0000;
  }

}

bool BlockStream::acquireSync() {

  if (is_in_sync_)
    return true;

  // Try to find the repeating offset sequence
  if (received_offset_ != OFFSET_INVALID) {
    int dist = bitcount_ - prevbitcount_;

    if (dist % 26 == 0 && dist <= 156 &&
        (block_number_for_offset[prevsync_] + dist/26) % 4 ==
        block_number_for_offset[received_offset_]) {
      is_in_sync_ = true;
      expected_offset_ = received_offset_;
    } else {
      prevbitcount_ = bitcount_;
      prevsync_ = received_offset_;
    }
  }

  return is_in_sync_;

}

Group BlockStream::getNextGroup() {

  Group group;

  while (!isEOF()) {

    // Compensate for clock slip corrections
    bitcount_ += 26 - left_to_read_;

    // Read from radio
    for (int i=0; i < (is_in_sync_ ? left_to_read_ : 1); i++, bitcount_++) {
      wideblock_ = (wideblock_ << 1) + getNextBit();
    }

    left_to_read_ = 26;
    wideblock_ &= kBitmask28;

    uint32_t block = (wideblock_ >> 1) & kBitmask26;

    uint16_t syndrome = calcSyndrome(block);
    received_offset_ = (offset_syndromes.count(syndrome) > 0 ?
        offset_syndromes.at(syndrome) : OFFSET_INVALID);

    if (!acquireSync())
      continue;

    block_counter_++;
    uint16_t message = block >> 10;
    bool was_valid_word = true;

    if (expected_offset_ == OFFSET_C && received_offset_ == OFFSET_CI)
      expected_offset_ = OFFSET_CI;

    block_has_errors_[block_counter_ % block_has_errors_.size()] = false;

    if (received_offset_ != expected_offset_) {

      block_has_errors_[block_counter_ % block_has_errors_.size()] = true;

      was_valid_word = false;

      // Detect & correct clock slips (Section C.1.2)
      if (expected_offset_ == OFFSET_A && pi_ != 0 &&
          ((wideblock_ >> 12) & kBitmask16) == pi_) {
        message = pi_;
        wideblock_ >>= 1;
        received_offset_ = OFFSET_A;
      } else if (expected_offset_ == OFFSET_A && pi_ != 0 &&
          ((wideblock_ >> 10) & kBitmask16) == pi_) {
        message = pi_;
        wideblock_ = (wideblock_ << 1) + getNextBit();
        received_offset_ = OFFSET_A;
        left_to_read_ = 25;

      } else {

        block = correctBurstErrors(block);
        if (calcSyndrome(block) == 0x000) {
          message = block >> 10;
          received_offset_ = expected_offset_;
        }

      }

      // Still no valid syndrome
      if (received_offset_ != expected_offset_)
        uncorrectable();
    }

    // Error-free block received

    if (received_offset_ == expected_offset_) {

      group.block[expected_offset_] = message;
      group.hasOffset[expected_offset_] = true;

      if (expected_offset_ == OFFSET_A || expected_offset_ == OFFSET_CI) {
        group.pi = message;
        group.hasPi = true;
        if (was_valid_word)
          pi_ = message;
      }

    }

    expected_offset_ = nextOffsetFor(expected_offset_);

    if (expected_offset_ == OFFSET_A) {
      break;
    }

  }

  if (group.hasOffset[OFFSET_B]) {
    group.type = GroupType(bits(group.block[OFFSET_B], 11, 5));
    group.hasType = true;
  } else if (group.hasOffset[OFFSET_CI] && group.hasOffset[OFFSET_D]) {
    GroupType potential(bits(group.block[OFFSET_D], 11, 5));
    if (potential.num == 15 && potential.ab == VERSION_B) {
      group.type = potential;
      group.hasType = true;
    }
  }

  return group;

}

bool BlockStream::isEOF() const {
  return is_eof_;
}

#ifdef DEBUG
float BlockStream::getT() const {
  return subcarrier_.getT();
}
#endif

} // namespace redsea
<commit_msg>whitespace<commit_after>#include "src/block_sync.h"

#include <vector>

#include "src/util.h"

namespace redsea {

namespace {

const unsigned kBitmask16 = 0x000FFFF;
const unsigned kBitmask26 = 0x3FFFFFF;
const unsigned kBitmask28 = 0xFFFFFFF;

// "...the error-correction system should be enabled, but should be restricted
// by attempting to correct bursts of errors spanning one or two bits."
// Kopitz & Marks 1999: "RDS: The Radio Data System", p. 224
const unsigned kMaxErrorLength = 2;

const std::vector<uint16_t> offset_words =
    {0x0FC, 0x198, 0x168, 0x350, 0x1B4};
const std::map<uint16_t,eOffset> offset_syndromes =
    {{0x3D8,OFFSET_A},  {0x3D4,OFFSET_B}, {0x25C,OFFSET_C},
     {0x3CC,OFFSET_CI}, {0x258,OFFSET_D}};
const std::vector<uint16_t> block_number_for_offset =
    {0, 1, 2, 2, 3};

// Section B.1.1: '-- calculated by the modulo-two addition of all the rows of
// the -- matrix for which the corresponding coefficient in the -- vector is 1.'
uint32_t matrixMultiply(uint32_t vec, const std::vector<uint32_t>& matrix) {
  uint32_t result = 0;

  for (size_t k=0; k < matrix.size(); k++)
    if ((vec >> k) & 0x01)
      result ^= matrix[matrix.size() - 1 - k];

  return result;
}

// Section B.2.1: 'The calculation of the syndromes -- can easily be done by
// multiplying each word with the parity matrix H.'
uint32_t calcSyndrome(uint32_t vec) {
  static const std::vector<uint32_t> parity_check_matrix({
      0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004,
      0x002, 0x001, 0x2dc, 0x16e, 0x0b7, 0x287, 0x39f, 0x313,
      0x355, 0x376, 0x1bb, 0x201, 0x3dc,
      0x1ee, 0x0f7, 0x2a7, 0x38f, 0x31b
  });

  return matrixMultiply(vec, parity_check_matrix);
}

eOffset nextOffsetFor(eOffset o) {
  static const std::map<eOffset,eOffset> next_offset({
      {OFFSET_A, OFFSET_B}, {OFFSET_B, OFFSET_C},
      {OFFSET_C, OFFSET_D}, {OFFSET_CI, OFFSET_D},
      {OFFSET_D, OFFSET_A}
  });
  return next_offset.at(o);
}

// Precompute mapping of syndromes to error vectors
std::map<uint16_t,uint32_t> makeErrorLookupTable() {
  std::map<uint16_t,uint32_t> result;

  for (uint32_t e=1; e < (1 << kMaxErrorLength); e++) {
    for (unsigned shift=0; shift < 26; shift++) {
      uint32_t errvec = ((e << shift) & kBitmask26);

      uint32_t sy = calcSyndrome(errvec);
      result[sy] = errvec;
    }
  }
  return result;
}

}  // namespace

BlockStream::BlockStream(eInputType input_type, bool has_echo) : bitcount_(0),
  prevbitcount_(0), left_to_read_(0), wideblock_(0), prevsync_(0),
  block_counter_(0), expected_offset_(OFFSET_A),
  received_offset_(OFFSET_INVALID), pi_(0), is_in_sync_(false),
  block_has_errors_(50),
#ifdef HAVE_LIQUID
  subcarrier_(has_echo),
#endif
  ascii_bits_(has_echo),
  error_lookup_(makeErrorLookupTable()),
  input_type_(input_type), is_eof_(false) {

}

int BlockStream::getNextBit() {
  int result = 0;
#ifdef HAVE_LIQUID
  if (input_type_ == INPUT_MPX) {
    result = subcarrier_.getNextBit();
    is_eof_ = subcarrier_.isEOF();
  }
#endif
  if (input_type_ == INPUT_ASCIIBITS) {
    result = ascii_bits_.getNextBit();
    is_eof_ = ascii_bits_.isEOF();
  }

  return result;
}

// Section B.2.2
uint32_t BlockStream::correctBurstErrors(uint32_t block) const {
  uint16_t synd_reg =
    calcSyndrome(block ^ offset_words[expected_offset_]);

  uint32_t corrected_block = block;

  if (error_lookup_.find(synd_reg) != error_lookup_.end()) {
    corrected_block = (block ^ offset_words[expected_offset_])
      ^ (error_lookup_.at(synd_reg));
  }

  return corrected_block;

}

// A block can't be decoded
void BlockStream::uncorrectable() {
  block_has_errors_[block_counter_ % block_has_errors_.size()] = true;

  unsigned num_erroneous_blocks = 0;
  for (bool e : block_has_errors_) {
    if (e)
      num_erroneous_blocks++;
  }

  // Sync is lost when >45 out of last 50 blocks are erroneous (Section C.1.2)
  if (is_in_sync_ && num_erroneous_blocks > 45) {
    is_in_sync_ = false;
    for (size_t i=0; i < block_has_errors_.size(); i++)
      block_has_errors_[i] = false;
    pi_ = 0x0000;
  }
}

bool BlockStream::acquireSync() {
  if (is_in_sync_)
    return true;

  // Try to find the repeating offset sequence
  if (received_offset_ != OFFSET_INVALID) {
    int dist = bitcount_ - prevbitcount_;

    if (dist % 26 == 0 && dist <= 156 &&
        (block_number_for_offset[prevsync_] + dist/26) % 4 ==
        block_number_for_offset[received_offset_]) {
      is_in_sync_ = true;
      expected_offset_ = received_offset_;
    } else {
      prevbitcount_ = bitcount_;
      prevsync_ = received_offset_;
    }
  }

  return is_in_sync_;
}

Group BlockStream::getNextGroup() {
  Group group;

  while (!isEOF()) {

    // Compensate for clock slip corrections
    bitcount_ += 26 - left_to_read_;

    // Read from radio
    for (int i=0; i < (is_in_sync_ ? left_to_read_ : 1); i++, bitcount_++) {
      wideblock_ = (wideblock_ << 1) + getNextBit();
    }

    left_to_read_ = 26;
    wideblock_ &= kBitmask28;

    uint32_t block = (wideblock_ >> 1) & kBitmask26;

    uint16_t syndrome = calcSyndrome(block);
    received_offset_ = (offset_syndromes.count(syndrome) > 0 ?
        offset_syndromes.at(syndrome) : OFFSET_INVALID);

    if (!acquireSync())
      continue;

    block_counter_++;
    uint16_t message = block >> 10;
    bool was_valid_word = true;

    if (expected_offset_ == OFFSET_C && received_offset_ == OFFSET_CI)
      expected_offset_ = OFFSET_CI;

    block_has_errors_[block_counter_ % block_has_errors_.size()] = false;

    if (received_offset_ != expected_offset_) {

      block_has_errors_[block_counter_ % block_has_errors_.size()] = true;

      was_valid_word = false;

      // Detect & correct clock slips (Section C.1.2)
      if (expected_offset_ == OFFSET_A && pi_ != 0 &&
          ((wideblock_ >> 12) & kBitmask16) == pi_) {
        message = pi_;
        wideblock_ >>= 1;
        received_offset_ = OFFSET_A;
      } else if (expected_offset_ == OFFSET_A && pi_ != 0 &&
          ((wideblock_ >> 10) & kBitmask16) == pi_) {
        message = pi_;
        wideblock_ = (wideblock_ << 1) + getNextBit();
        received_offset_ = OFFSET_A;
        left_to_read_ = 25;

      } else {

        block = correctBurstErrors(block);
        if (calcSyndrome(block) == 0x000) {
          message = block >> 10;
          received_offset_ = expected_offset_;
        }
      }

      // Still no valid syndrome
      if (received_offset_ != expected_offset_)
        uncorrectable();
    }

    // Error-free block received

    if (received_offset_ == expected_offset_) {

      group.block[expected_offset_] = message;
      group.hasOffset[expected_offset_] = true;

      if (expected_offset_ == OFFSET_A || expected_offset_ == OFFSET_CI) {
        group.pi = message;
        group.hasPi = true;
        if (was_valid_word)
          pi_ = message;
      }
    }

    expected_offset_ = nextOffsetFor(expected_offset_);

    if (expected_offset_ == OFFSET_A) {
      break;
    }
  }

  if (group.hasOffset[OFFSET_B]) {
    group.type = GroupType(bits(group.block[OFFSET_B], 11, 5));
    group.hasType = true;
  } else if (group.hasOffset[OFFSET_CI] && group.hasOffset[OFFSET_D]) {
    GroupType potential(bits(group.block[OFFSET_D], 11, 5));
    if (potential.num == 15 && potential.ab == VERSION_B) {
      group.type = potential;
      group.hasType = true;
    }
  }

  return group;
}

bool BlockStream::isEOF() const {
  return is_eof_;
}

#ifdef DEBUG
float BlockStream::getT() const {
  return subcarrier_.getT();
}
#endif

}  // namespace redsea
<|endoftext|>


<commit_before>/*
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "schema.h"
#include "user_def.h"
#include "engine.h"
#include "space.h"
#include "func.h"
#include "tuple.h"
#include "assoc.h"
#include "lua/utils.h"
#include "lua/space.h"
#include "key_def.h"
#include "alter.h"
#include "scoped_guard.h"
#include <stdio.h>
/**
 * @module Data Dictionary
 *
 * The data dictionary is responsible for storage and caching
 * of system metadata, such as information about existing
 * spaces, indexes, tuple formats. Space and index metadata
 * is called in dedicated spaces, _space and _index respectively.
 * The contents of these spaces is fully cached in a cache of
 * struct space objects.
 *
 * struct space is an in-memory instance representing a single
 * space with its metadata, space data, and methods to manage
 * it.
 */

/** All existing spaces. */
static struct mh_i32ptr_t *spaces;
static struct mh_i32ptr_t *funcs;
int sc_version;

bool
space_is_system(struct space *space)
{
	return space->def.id > SC_SYSTEM_ID_MIN &&
		space->def.id < SC_SYSTEM_ID_MAX;
}

/** Return space by its number */
extern "C" struct space *
space_by_id(uint32_t id)
{
	mh_int_t space = mh_i32ptr_find(spaces, id, NULL);
	if (space == mh_end(spaces))
		return NULL;
	return (struct space *) mh_i32ptr_node(spaces, space)->val;
}

extern "C" const char *
space_name_by_id(uint32_t id)
{
	struct space *space = space_by_id(id);
	return space ? space_name(space) : "";
}

/**
 * Visit all spaces and apply 'func'.
 */
void
space_foreach(void (*func)(struct space *sp, void *udata), void *udata)
{
	mh_int_t i;
	struct space *space;
	char key[6];
	assert(mp_sizeof_uint(SC_SYSTEM_ID_MIN) <= sizeof(key));
	mp_encode_uint(key, SC_SYSTEM_ID_MIN);

	/*
	 * Make sure we always visit system spaces first,
	 * in order from lowest space id to the highest..
	 * This is essential for correctly recovery from the
	 * snapshot, and harmless otherwise.
	 */
	space = space_by_id(SC_SPACE_ID);
	Index *pk = space ? space_index(space, 0) : NULL;
	if (pk) {
		struct iterator *it = pk->allocIterator();
		auto scoped_guard = make_scoped_guard([=] { it->free(it); });
		pk->initIterator(it, ITER_GE, key, 1);
		IteratorGuard it_guard(it);

		struct tuple *tuple;
		while ((tuple = it->next(it))) {
			/* Get space id, primary key, field 0. */
			uint32_t id = tuple_field_u32(tuple, 0);
			space = space_cache_find(id);
			if (! space_is_system(space))
				break;
			func(space, udata);
		}
	}

	mh_foreach(spaces, i) {
		space = (struct space *) mh_i32ptr_node(spaces, i)->val;
		if (space_is_system(space))
			continue;
		func(space, udata);
	}
}

/** Delete a space from the space cache and Lua. */
struct space *
space_cache_delete(uint32_t id)
{
	if (tarantool_L)
		box_lua_space_delete(tarantool_L, id);
	mh_int_t k = mh_i32ptr_find(spaces, id, NULL);
	assert(k != mh_end(spaces));
	struct space *space = (struct space *)mh_i32ptr_node(spaces, k)->val;
	mh_i32ptr_del(spaces, k, NULL);
	sc_version++;
	return space;
}

/**
 * Update the space in the space cache and in Lua. Returns
 * the old space instance, if any, or NULL if it's a new space.
 */
struct space *
space_cache_replace(struct space *space)
{
	const struct mh_i32ptr_node_t node = { space_id(space), space };
	struct mh_i32ptr_node_t old, *p_old = &old;
	mh_int_t k = mh_i32ptr_put(spaces, &node, &p_old, NULL);
	if (k == mh_end(spaces)) {
		panic_syserror("Out of memory for the data "
			       "dictionary cache.");
	}
	sc_version++;
	/*
	 * Must be after the space is put into the hash, since
	 * box.schema.space.bless() uses hash look up to find the
	 * space and create userdata objects for space objects.
	 */
	box_lua_space_new(tarantool_L, space);
	return p_old ? (struct space *) p_old->val : NULL;
}

/** A wrapper around space_new() for data dictionary spaces. */
struct space *
sc_space_new(struct space_def *space_def,
	     struct key_def *key_def,
	     struct trigger *trigger)
{
	struct rlist key_list;
	rlist_create(&key_list);
	rlist_add_entry(&key_list, key_def, link);
	struct space *space = space_new(space_def, &key_list);
	(void) space_cache_replace(space);
	if (trigger)
		trigger_add(&space->on_replace, trigger);
	/*
	 * Data dictionary spaces are fully built since:
	 * - they contain data right from the start
	 * - they are fully operable already during recovery
	 * - if there is a record in the snapshot which mandates
	 *   addition of a new index to a system space, this
	 *   index is built tuple-by-tuple, not in bulk, which
	 *   ensures validation of tuples when starting from
	 *   a snapshot of older version.
	 */
	space->handler->engine->initSystemSpace(space);
	return space;
}

uint32_t
schema_find_id(uint32_t system_space_id, uint32_t index_id,
	       const char *name, uint32_t len)
{
	struct space *space = space_cache_find(system_space_id);
	Index *index = index_find(space, index_id);
	struct iterator *it = index->position();
	char key[5 /* str len */ + BOX_NAME_MAX];
	mp_encode_str(key, name, len);
	index->initIterator(it, ITER_EQ, key, 1);
	IteratorGuard it_guard(it);

	struct tuple *tuple;
	while ((tuple = it->next(it))) {
		/* id is always field #1 */
		return tuple_field_u32(tuple, 0);
	}
	return SC_ID_NIL;
}

/**
 * Initialize a prototype for the two mandatory data
 * dictionary spaces and create a cache entry for them.
 * When restoring data from the snapshot these spaces
 * will get altered automatically to their actual format.
 */
void
schema_init()
{
	/* Initialize the space cache. */
	spaces = mh_i32ptr_new();
	funcs = mh_i32ptr_new();
	/*
	 * Create surrogate space objects for the mandatory system
	 * spaces (the primal eggs from which we get all the
	 * chicken). Their definitions will be overwritten by the
	 * data in the snapshot, and they will thus be
	 * *re-created* during recovery.  Note, the index type
	 * must be TREE and space identifiers must be the smallest
	 * one to ensure that these spaces are always recovered
	 * (and re-created) first.
	 */
	/* _schema - key/value space with schema description */
	struct space_def def = {
		SC_SCHEMA_ID, ADMIN, 0, "_schema", "memtx", false
	};
	struct key_def *key_def = key_def_new(def.id,
					      0 /* index id */,
					      "primary", /* name */
					      TREE /* index type */,
					      true /* unique */,
					      1 /* part count */);
	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, STRING);
	(void) sc_space_new(&def, key_def, &on_replace_schema);

	/* _space - home for all spaces. */
	key_def->space_id = def.id = SC_SPACE_ID;
	snprintf(def.name, sizeof(def.name), "_space");
	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, NUM);

	(void) sc_space_new(&def, key_def, &alter_space_on_replace_space);

	/* _user - all existing users */
	key_def->space_id = def.id = SC_USER_ID;
	snprintf(def.name, sizeof(def.name), "_user");
	(void) sc_space_new(&def, key_def, &on_replace_user);

	/* _func - all executable objects on which one can have grants */
	key_def->space_id = def.id = SC_FUNC_ID;
	snprintf(def.name, sizeof(def.name), "_func");
	(void) sc_space_new(&def, key_def, &on_replace_func);
	/*
	 * _priv - association user <-> object
	 * The real index is defined in the snapshot.
	 */
	key_def->space_id = def.id = SC_PRIV_ID;
	snprintf(def.name, sizeof(def.name), "_priv");
	(void) sc_space_new(&def, key_def, &on_replace_priv);
	/*
	 * _cluster - association server uuid <-> server id
	 * The real index is defined in the snapshot.
	 */
	key_def->space_id = def.id = SC_CLUSTER_ID;
	snprintf(def.name, sizeof(def.name), "_cluster");
	(void) sc_space_new(&def, key_def, &on_replace_cluster);
	key_def_delete(key_def);

	/* _index - definition of indexes in all spaces */
	def.id = SC_INDEX_ID;
	snprintf(def.name, sizeof(def.name), "_index");
	key_def = key_def_new(def.id,
			      0 /* index id */,
			      "primary",
			      TREE /* index type */,
			      true /* unique */,
			      2 /* part count */);
	/* space no */
	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, NUM);
	/* index no */
	key_def_set_part(key_def, 1 /* part no */, 1 /* field no */, NUM);
	(void) sc_space_new(&def, key_def, &alter_space_on_replace_index);
	key_def_delete(key_def);
}

void
schema_free(void)
{
	if (spaces == NULL)
		return;
	while (mh_size(spaces) > 0) {
		mh_int_t i = mh_first(spaces);

		struct space *space = (struct space *)
				mh_i32ptr_node(spaces, i)->val;
		space_cache_delete(space_id(space));
		space_delete(space);
	}
	mh_i32ptr_delete(spaces);
	while (mh_size(funcs) > 0) {
		mh_int_t i = mh_first(funcs);

		struct func *func = ((struct func *)
				     mh_i32ptr_node(funcs, i)->val);
		func_cache_delete(func->def.fid);
	}
	mh_i32ptr_delete(funcs);
}

void
func_cache_replace(struct func_def *def)
{
	struct func *old = func_by_id(def->fid);
	if (old) {
		func_update(old, def);
		return;
	}
	if (mh_size(funcs) >= BOX_FUNCTION_MAX)
		tnt_raise(ClientError, ER_FUNCTION_MAX, BOX_FUNCTION_MAX);
	struct func *func = func_new(def);
	if (func == NULL) {
error:
		panic_syserror("Out of memory for the data "
			       "dictionary cache (stored function).");
	}
	const struct mh_i32ptr_node_t node = { def->fid, func };
	mh_int_t k = mh_i32ptr_put(funcs, &node, NULL, NULL);
	if (k == mh_end(funcs)) {
		func_delete(func);
		goto error;
	}
}

void
func_cache_delete(uint32_t fid)
{
	mh_int_t k = mh_i32ptr_find(funcs, fid, NULL);
	if (k == mh_end(funcs))
		return;
	struct func *func = (struct func *)
		mh_i32ptr_node(funcs, k)->val;
	mh_i32ptr_del(funcs, k, NULL);
	func_delete(func);
}

struct func *
func_by_id(uint32_t fid)
{
	mh_int_t func = mh_i32ptr_find(funcs, fid, NULL);
	if (func == mh_end(funcs))
		return NULL;
	return (struct func *) mh_i32ptr_node(funcs, func)->val;
}

bool
schema_find_grants(const char *type, uint32_t id)
{
	struct space *priv = space_cache_find(SC_PRIV_ID);
	/** "object" index */
	Index *index = index_find(priv, 2);
	struct iterator *it = index->position();
	char key[10 + BOX_NAME_MAX];
	mp_encode_uint(mp_encode_str(key, type, strlen(type)), id);
	index->initIterator(it, ITER_EQ, key, 2);
	IteratorGuard it_guard(it);
	return it->next(it);
}
<commit_msg>fixed gh-899 : added dynamic length buffer for msgpack string<commit_after>/*
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "schema.h"
#include "user_def.h"
#include "engine.h"
#include "space.h"
#include "func.h"
#include "tuple.h"
#include "assoc.h"
#include "lua/utils.h"
#include "lua/space.h"
#include "key_def.h"
#include "alter.h"
#include "scoped_guard.h"
#include <stdio.h>
/**
 * @module Data Dictionary
 *
 * The data dictionary is responsible for storage and caching
 * of system metadata, such as information about existing
 * spaces, indexes, tuple formats. Space and index metadata
 * is called in dedicated spaces, _space and _index respectively.
 * The contents of these spaces is fully cached in a cache of
 * struct space objects.
 *
 * struct space is an in-memory instance representing a single
 * space with its metadata, space data, and methods to manage
 * it.
 */

/** All existing spaces. */
static struct mh_i32ptr_t *spaces;
static struct mh_i32ptr_t *funcs;
int sc_version;

bool
space_is_system(struct space *space)
{
	return space->def.id > SC_SYSTEM_ID_MIN &&
		space->def.id < SC_SYSTEM_ID_MAX;
}

/** Return space by its number */
extern "C" struct space *
space_by_id(uint32_t id)
{
	mh_int_t space = mh_i32ptr_find(spaces, id, NULL);
	if (space == mh_end(spaces))
		return NULL;
	return (struct space *) mh_i32ptr_node(spaces, space)->val;
}

extern "C" const char *
space_name_by_id(uint32_t id)
{
	struct space *space = space_by_id(id);
	return space ? space_name(space) : "";
}

/**
 * Visit all spaces and apply 'func'.
 */
void
space_foreach(void (*func)(struct space *sp, void *udata), void *udata)
{
	mh_int_t i;
	struct space *space;
	char key[6];
	assert(mp_sizeof_uint(SC_SYSTEM_ID_MIN) <= sizeof(key));
	mp_encode_uint(key, SC_SYSTEM_ID_MIN);

	/*
	 * Make sure we always visit system spaces first,
	 * in order from lowest space id to the highest..
	 * This is essential for correctly recovery from the
	 * snapshot, and harmless otherwise.
	 */
	space = space_by_id(SC_SPACE_ID);
	Index *pk = space ? space_index(space, 0) : NULL;
	if (pk) {
		struct iterator *it = pk->allocIterator();
		auto scoped_guard = make_scoped_guard([=] { it->free(it); });
		pk->initIterator(it, ITER_GE, key, 1);
		IteratorGuard it_guard(it);

		struct tuple *tuple;
		while ((tuple = it->next(it))) {
			/* Get space id, primary key, field 0. */
			uint32_t id = tuple_field_u32(tuple, 0);
			space = space_cache_find(id);
			if (! space_is_system(space))
				break;
			func(space, udata);
		}
	}

	mh_foreach(spaces, i) {
		space = (struct space *) mh_i32ptr_node(spaces, i)->val;
		if (space_is_system(space))
			continue;
		func(space, udata);
	}
}

/** Delete a space from the space cache and Lua. */
struct space *
space_cache_delete(uint32_t id)
{
	if (tarantool_L)
		box_lua_space_delete(tarantool_L, id);
	mh_int_t k = mh_i32ptr_find(spaces, id, NULL);
	assert(k != mh_end(spaces));
	struct space *space = (struct space *)mh_i32ptr_node(spaces, k)->val;
	mh_i32ptr_del(spaces, k, NULL);
	sc_version++;
	return space;
}

/**
 * Update the space in the space cache and in Lua. Returns
 * the old space instance, if any, or NULL if it's a new space.
 */
struct space *
space_cache_replace(struct space *space)
{
	const struct mh_i32ptr_node_t node = { space_id(space), space };
	struct mh_i32ptr_node_t old, *p_old = &old;
	mh_int_t k = mh_i32ptr_put(spaces, &node, &p_old, NULL);
	if (k == mh_end(spaces)) {
		panic_syserror("Out of memory for the data "
			       "dictionary cache.");
	}
	sc_version++;
	/*
	 * Must be after the space is put into the hash, since
	 * box.schema.space.bless() uses hash look up to find the
	 * space and create userdata objects for space objects.
	 */
	box_lua_space_new(tarantool_L, space);
	return p_old ? (struct space *) p_old->val : NULL;
}

/** A wrapper around space_new() for data dictionary spaces. */
struct space *
sc_space_new(struct space_def *space_def,
	     struct key_def *key_def,
	     struct trigger *trigger)
{
	struct rlist key_list;
	rlist_create(&key_list);
	rlist_add_entry(&key_list, key_def, link);
	struct space *space = space_new(space_def, &key_list);
	(void) space_cache_replace(space);
	if (trigger)
		trigger_add(&space->on_replace, trigger);
	/*
	 * Data dictionary spaces are fully built since:
	 * - they contain data right from the start
	 * - they are fully operable already during recovery
	 * - if there is a record in the snapshot which mandates
	 *   addition of a new index to a system space, this
	 *   index is built tuple-by-tuple, not in bulk, which
	 *   ensures validation of tuples when starting from
	 *   a snapshot of older version.
	 */
	space->handler->engine->initSystemSpace(space);
	return space;
}

uint32_t
schema_find_id(uint32_t system_space_id, uint32_t index_id,
	       const char *name, uint32_t len)
{
	struct space *space = space_cache_find(system_space_id);
	Index *index = index_find(space, index_id);

	const uint32_t local_len = BOX_NAME_MAX * 2;
	char buf[5 + local_len];
	char *key = len <= local_len ? buf : (char *)malloc(5 + len);
	auto key_guard =
		make_scoped_guard([key, &buf]{ if (key != buf) free(key); });
	mp_encode_str(key, name, len);

	struct iterator *it = index->position();
	index->initIterator(it, ITER_EQ, key, 1);
	IteratorGuard it_guard(it);

	struct tuple *tuple = it->next(it);
	if (tuple) {
		/* id is always field #1 */
		return tuple_field_u32(tuple, 0);
	}
	return SC_ID_NIL;
}

/**
 * Initialize a prototype for the two mandatory data
 * dictionary spaces and create a cache entry for them.
 * When restoring data from the snapshot these spaces
 * will get altered automatically to their actual format.
 */
void
schema_init()
{
	/* Initialize the space cache. */
	spaces = mh_i32ptr_new();
	funcs = mh_i32ptr_new();
	/*
	 * Create surrogate space objects for the mandatory system
	 * spaces (the primal eggs from which we get all the
	 * chicken). Their definitions will be overwritten by the
	 * data in the snapshot, and they will thus be
	 * *re-created* during recovery.  Note, the index type
	 * must be TREE and space identifiers must be the smallest
	 * one to ensure that these spaces are always recovered
	 * (and re-created) first.
	 */
	/* _schema - key/value space with schema description */
	struct space_def def = {
		SC_SCHEMA_ID, ADMIN, 0, "_schema", "memtx", false
	};
	struct key_def *key_def = key_def_new(def.id,
					      0 /* index id */,
					      "primary", /* name */
					      TREE /* index type */,
					      true /* unique */,
					      1 /* part count */);
	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, STRING);
	(void) sc_space_new(&def, key_def, &on_replace_schema);

	/* _space - home for all spaces. */
	key_def->space_id = def.id = SC_SPACE_ID;
	snprintf(def.name, sizeof(def.name), "_space");
	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, NUM);

	(void) sc_space_new(&def, key_def, &alter_space_on_replace_space);

	/* _user - all existing users */
	key_def->space_id = def.id = SC_USER_ID;
	snprintf(def.name, sizeof(def.name), "_user");
	(void) sc_space_new(&def, key_def, &on_replace_user);

	/* _func - all executable objects on which one can have grants */
	key_def->space_id = def.id = SC_FUNC_ID;
	snprintf(def.name, sizeof(def.name), "_func");
	(void) sc_space_new(&def, key_def, &on_replace_func);
	/*
	 * _priv - association user <-> object
	 * The real index is defined in the snapshot.
	 */
	key_def->space_id = def.id = SC_PRIV_ID;
	snprintf(def.name, sizeof(def.name), "_priv");
	(void) sc_space_new(&def, key_def, &on_replace_priv);
	/*
	 * _cluster - association server uuid <-> server id
	 * The real index is defined in the snapshot.
	 */
	key_def->space_id = def.id = SC_CLUSTER_ID;
	snprintf(def.name, sizeof(def.name), "_cluster");
	(void) sc_space_new(&def, key_def, &on_replace_cluster);
	key_def_delete(key_def);

	/* _index - definition of indexes in all spaces */
	def.id = SC_INDEX_ID;
	snprintf(def.name, sizeof(def.name), "_index");
	key_def = key_def_new(def.id,
			      0 /* index id */,
			      "primary",
			      TREE /* index type */,
			      true /* unique */,
			      2 /* part count */);
	/* space no */
	key_def_set_part(key_def, 0 /* part no */, 0 /* field no */, NUM);
	/* index no */
	key_def_set_part(key_def, 1 /* part no */, 1 /* field no */, NUM);
	(void) sc_space_new(&def, key_def, &alter_space_on_replace_index);
	key_def_delete(key_def);
}

void
schema_free(void)
{
	if (spaces == NULL)
		return;
	while (mh_size(spaces) > 0) {
		mh_int_t i = mh_first(spaces);

		struct space *space = (struct space *)
				mh_i32ptr_node(spaces, i)->val;
		space_cache_delete(space_id(space));
		space_delete(space);
	}
	mh_i32ptr_delete(spaces);
	while (mh_size(funcs) > 0) {
		mh_int_t i = mh_first(funcs);

		struct func *func = ((struct func *)
				     mh_i32ptr_node(funcs, i)->val);
		func_cache_delete(func->def.fid);
	}
	mh_i32ptr_delete(funcs);
}

void
func_cache_replace(struct func_def *def)
{
	struct func *old = func_by_id(def->fid);
	if (old) {
		func_update(old, def);
		return;
	}
	if (mh_size(funcs) >= BOX_FUNCTION_MAX)
		tnt_raise(ClientError, ER_FUNCTION_MAX, BOX_FUNCTION_MAX);
	struct func *func = func_new(def);
	if (func == NULL) {
error:
		panic_syserror("Out of memory for the data "
			       "dictionary cache (stored function).");
	}
	const struct mh_i32ptr_node_t node = { def->fid, func };
	mh_int_t k = mh_i32ptr_put(funcs, &node, NULL, NULL);
	if (k == mh_end(funcs)) {
		func_delete(func);
		goto error;
	}
}

void
func_cache_delete(uint32_t fid)
{
	mh_int_t k = mh_i32ptr_find(funcs, fid, NULL);
	if (k == mh_end(funcs))
		return;
	struct func *func = (struct func *)
		mh_i32ptr_node(funcs, k)->val;
	mh_i32ptr_del(funcs, k, NULL);
	func_delete(func);
}

struct func *
func_by_id(uint32_t fid)
{
	mh_int_t func = mh_i32ptr_find(funcs, fid, NULL);
	if (func == mh_end(funcs))
		return NULL;
	return (struct func *) mh_i32ptr_node(funcs, func)->val;
}

bool
schema_find_grants(const char *type, uint32_t id)
{
	struct space *priv = space_cache_find(SC_PRIV_ID);
	/** "object" index */
	Index *index = index_find(priv, 2);
	struct iterator *it = index->position();
	char key[10 + BOX_NAME_MAX];
	mp_encode_uint(mp_encode_str(key, type, strlen(type)), id);
	index->initIterator(it, ITER_EQ, key, 2);
	IteratorGuard it_guard(it);
	return it->next(it);
}
<|endoftext|>


<commit_before>//
// Created by mistlight on 1/28/2017.
//

#include <src/core/tasks/PlayerScanTask.h>
#include <QDebug>
#include <include/vlc/vlc.h>
#include <src/simple-lib/ThreadPool.h>
#include <QThread>
#include "ScanPlayer.h"

Core::ScanPlayer::ScanPlayer() {
    /* Load the VLC engine */
    this->inst = libvlc_new(0, NULL);
    if(this->inst == NULL) {
        qWarning() << "ERROR";
        throw "Exception has occured, cannot init vlc engine from Scanning";
    }

    this->mediaPlayer = libvlc_media_player_new(this->inst);
    if(this->mediaPlayer == NULL) {
        qWarning() << "ERROR: Could not create media player in Scanning";
    }

    this->threadPool = std::unique_ptr<ThreadPool>(new ThreadPool(1));
    this->hasScanFinished = true;
}

void Core::ScanPlayer::addAudiobook(std::shared_ptr<AudiobookProxy> audiobook) {
    qDebug() << "Scan audiobook called";
    auto fileList = audiobook->getFilesForAudiobook();
    this->mutex.lock();
    for(int i = 0; i < fileList.size(); i++) {
        this->fileQueue.push(fileList[i]);
    }
    this->mutex.unlock();

    this->startScanTask(audiobook);
}

void Core::ScanPlayer::addAudiobookFile(std::shared_ptr<AudiobookFileProxy> file) {
    // don't need to call this function on already scanned items
    if(file->getMediaDuration() > 0) {
        return;
    }

    this->mutex.lock();
    this->fileQueue.push(file);
    this->mutex.unlock();

    this->startScanTask(nullptr);
}

void Core::ScanPlayer::startScanTask(std::shared_ptr<AudiobookProxy> audiobook) {
    auto scanTask = new PlayerScanTask(this, audiobook);
    this->scanThread.start(scanTask);
}

void Core::ScanPlayer::performScan() {
    this->mutex.lock();

    qDebug() << "Scan task started";

    while(!this->fileQueue.empty()) {
        this->hasScanFinished = false;
        std::shared_ptr<AudiobookFileProxy> & element = this->fileQueue.front();
        this->currentlyScanning = element;
        qDebug() << "Currently scanning file: " << element->path();

        auto path =  element->path();
        auto currentFile = std::unique_ptr<QFile>(new QFile(path));

        if(!currentFile->open(QIODevice::ReadWrite)) {
            qDebug() << "QFILE FAILED!: " << path;
            return;
        }

        this->mediaItem = libvlc_media_new_fd(this->inst, currentFile->handle());
        if(this->mediaItem == NULL) {
            return;
        }

        libvlc_media_player_set_media(this->mediaPlayer, this->mediaItem);
        libvlc_media_player_play(this->mediaPlayer);

        libvlc_event_manager_t* eventManager = libvlc_media_event_manager(this->mediaItem);

        libvlc_event_attach(eventManager,
                            libvlc_MediaStateChanged,
                            (libvlc_callback_t) [](const struct libvlc_event_t * event, void *data) {
                                auto newState = event->u.media_state_changed.new_state;
                                if(newState == libvlc_Playing) {
                                    auto player = reinterpret_cast<ScanPlayer*>(data);

                                    player->threadPool->enqueue([player]() {
                                        long long duration = libvlc_media_get_duration(player->mediaItem);
                                        if(duration == -1) {
                                            player->currentlyScanning->setMediaDuration(duration);
                                        } else {
                                            qWarning() << "performScan() failed for: " << player->currentlyScanning->path();
                                        }

                                        libvlc_media_player_stop(player->mediaPlayer);

                                        player->hasScanFinished = true;
                                        qDebug() << "scan finisehd for file: " << player->currentlyScanning->path();
                                    });
                                }
                            },
                            this);

        while(!hasScanFinished) {
            QThread::msleep(10);
        }

        this->fileQueue.pop();
    }

    this->mutex.unlock();
    qDebug() << "Scan task ended";

}

void Core::ScanPlayer::retrieveScanResults() {

}

<commit_msg>fix conditional<commit_after>//
// Created by mistlight on 1/28/2017.
//

#include <src/core/tasks/PlayerScanTask.h>
#include <QDebug>
#include <include/vlc/vlc.h>
#include <src/simple-lib/ThreadPool.h>
#include <QThread>
#include "ScanPlayer.h"

Core::ScanPlayer::ScanPlayer() {
    /* Load the VLC engine */
    this->inst = libvlc_new(0, NULL);
    if(this->inst == NULL) {
        qWarning() << "ERROR";
        throw "Exception has occured, cannot init vlc engine from Scanning";
    }

    this->mediaPlayer = libvlc_media_player_new(this->inst);
    if(this->mediaPlayer == NULL) {
        qWarning() << "ERROR: Could not create media player in Scanning";
    }

    this->threadPool = std::unique_ptr<ThreadPool>(new ThreadPool(1));
    this->hasScanFinished = true;
}

void Core::ScanPlayer::addAudiobook(std::shared_ptr<AudiobookProxy> audiobook) {
    qDebug() << "Scan audiobook called";
    auto fileList = audiobook->getFilesForAudiobook();
    this->mutex.lock();
    for(int i = 0; i < fileList.size(); i++) {
        this->fileQueue.push(fileList[i]);
    }
    this->mutex.unlock();

    this->startScanTask(audiobook);
}

void Core::ScanPlayer::addAudiobookFile(std::shared_ptr<AudiobookFileProxy> file) {
    // don't need to call this function on already scanned items
    if(file->getMediaDuration() > 0) {
        return;
    }

    this->mutex.lock();
    this->fileQueue.push(file);
    this->mutex.unlock();

    this->startScanTask(nullptr);
}

void Core::ScanPlayer::startScanTask(std::shared_ptr<AudiobookProxy> audiobook) {
    auto scanTask = new PlayerScanTask(this, audiobook);
    this->scanThread.start(scanTask);
}

void Core::ScanPlayer::performScan() {
    this->mutex.lock();

    qDebug() << "Scan task started";

    while(!this->fileQueue.empty()) {
        this->hasScanFinished = false;
        std::shared_ptr<AudiobookFileProxy> & element = this->fileQueue.front();
        this->currentlyScanning = element;
        qDebug() << "Currently scanning file: " << element->path();

        auto path =  element->path();
        auto currentFile = std::unique_ptr<QFile>(new QFile(path));

        if(!currentFile->open(QIODevice::ReadWrite)) {
            qDebug() << "QFILE FAILED!: " << path;
            return;
        }

        this->mediaItem = libvlc_media_new_fd(this->inst, currentFile->handle());
        if(this->mediaItem == NULL) {
            return;
        }

        libvlc_media_player_set_media(this->mediaPlayer, this->mediaItem);
        libvlc_media_player_play(this->mediaPlayer);

        libvlc_event_manager_t* eventManager = libvlc_media_event_manager(this->mediaItem);

        libvlc_event_attach(eventManager,
                            libvlc_MediaParsedChanged,
                            (libvlc_callback_t) [](const struct libvlc_event_t * event, void *data) {
                                auto player = reinterpret_cast<ScanPlayer*>(data);
                                int parsedStatus = libvlc_media_is_parsed(player->mediaItem);

                                if(parsedStatus) {
                                    player->threadPool->enqueue([player]() {
                                        long long duration = libvlc_media_get_duration(player->mediaItem);
                                        if(duration > 0) {
                                            player->currentlyScanning->setMediaDuration(duration);
                                            qDebug() << "performScan() SUCCESS: " << player->currentlyScanning->path();
                                        } else {
                                            qWarning() << "performScan() failed for: " << player->currentlyScanning->path();
                                        }

                                        player->hasScanFinished = true;
                                    });
                                }
                            },
                            this);

        while(!hasScanFinished) {
            QThread::msleep(10);
        }

        libvlc_media_player_stop(this->mediaPlayer);

        this->fileQueue.pop();
    }

    this->mutex.unlock();
    qDebug() << "Scan task ended";

}

void Core::ScanPlayer::retrieveScanResults() {

}

<|endoftext|>