<commit_before>#include "writer/verilog/axi/master_port.h"

#include "design/design_util.h"
#include "iroha/insn_operands.h"
#include "iroha/i_design.h"
#include "iroha/logging.h"
#include "writer/module_template.h"
#include "writer/verilog/axi/master_controller.h"
#include "writer/verilog/embed.h"
#include "writer/verilog/insn_writer.h"
#include "writer/verilog/module.h"
#include "writer/verilog/ports.h"
#include "writer/verilog/shared_memory.h"
#include "writer/verilog/state.h"
#include "writer/verilog/table.h"

namespace iroha {
namespace writer {
namespace verilog {
namespace axi {

MasterPort::MasterPort(const IResource &res, const Table &table)
  : AxiPort(res, table) {
}

void MasterPort::BuildResource() {
  CHECK(tab_.GetITable() == res_.GetTable());
  string s = BuildPortToExt();
  BuildControllerInstance(s);
  if (!IsExclusiveAccessor()) {
    SharedMemory::BuildMemoryAccessorResource(*this, true, false,
					      res_.GetParentResource());
  }

  ostream &os = tmpl_->GetStream(kResourceSection);
  os << "  reg [31:0] " << AddrPort() << ";\n"
     << "  reg " << WenPort() << ";\n"
     << "  reg " << ReqPort() << ";\n"
     << "  wire " << AckPort() << ";\n";
  PortConfig cfg = AxiPort::GetPortConfig(res_);
  os << "  wire [" << (cfg.sram_addr_width - 1) << ":0] " << LenPort() << ";\n"
     << "  assign " << LenPort() << " = ~0;\n";

  ostream &is = tab_.InitialValueSectionStream();
  is << "      " << AddrPort() << " <= 0;\n"
     << "      " << WenPort() << " <= 0;\n"
     << "      " << ReqPort() << " <= 0;\n";

  map<IState *, IInsn *> accessors;
  CollectResourceCallers("*", &accessors);
  ostream &ss = tab_.StateOutputSectionStream();
  ss << "      " << ReqPort() << " <= ";
  if (accessors.size() == 0) {
    ss << "0;\n";
  } else {
    ss << JoinStatesWithSubState(accessors, 0) << ";\n";
  }
  map<IState *, IInsn *> writers;
  CollectResourceCallers("write", &writers);
  ss << "      " << WenPort() << " <= ";
  if (writers.size() == 0) {
    ss << "0;\n";
  } else {
    ss << JoinStatesWithSubState(writers, 0) << ";\n";
  }
}

void MasterPort::BuildInsn(IInsn *insn, State *st) {
    static const char I[] = "          ";
  string insn_st = InsnWriter::MultiCycleStateName(*(insn->GetResource()));
  ostream &os = st->StateBodySectionStream();
  os << I << "// AXI access request\n"
     << I << "if (" << insn_st << " == 0) begin\n"
     << I << "  " << AddrPort() << " <= "
     << InsnWriter::RegisterValue(*insn->inputs_[0], tab_.GetNames()) << ";\n"
     << I << "  if (" << AckPort() << ") begin\n"
     << I << "    " << insn_st << " <= 3;\n"
     << I << "  end\n"
     << I << "end\n";
}

string MasterPort::ControllerName(const IResource &res, bool reset_polarity) {
  const IResource *mem_res = res.GetParentResource();
  IArray *array = mem_res->GetArray();
  int addr_width = array->GetAddressWidth();
  string s = "axi_master_controller_a" + Util::Itoa(addr_width);
  bool r, w;
  GetReadWrite(res, &r, &w);
  if (r) {
    s += "r";
  }
  if (w) {
    s += "w";
  }
  return s;
}

void MasterPort::WriteController(const IResource &res,
				 bool reset_polarity,
				 ostream &os) {
  MasterController c(res, reset_polarity);
  c.Write(os);
}

void MasterPort::BuildControllerInstance(const string &wires) {
  tab_.GetEmbeddedModules()->RequestAxiMasterController(&res_, reset_polarity_);
  ostream &es = tmpl_->GetStream(kEmbeddedInstanceSection);
  string name = ControllerName(res_, reset_polarity_);
  es << "  " << name << " inst_" << name
     << "(";
  OutputSRAMConnection(es);
  es << ", .addr(" << AddrPort() << "), "
     << ".wen(" << WenPort() << "), "
     << ".req(" << ReqPort() << "), "
     << ".len(" << LenPort() << "), "
     << ".ack(" << AckPort() << ") "
     << wires
     << ");\n";
}

string MasterPort::BuildPortToExt() {
  Module *mod = tab_.GetModule();
  string s;
  PortConfig cfg = AxiPort::GetPortConfig(res_);
  MasterController::AddPorts(cfg, mod, true, true, &s);
  for (mod = mod->GetParentModule(); mod != nullptr;
       mod = mod->GetParentModule()) {
    MasterController::AddPorts(cfg, mod, true, true, nullptr);
  }
  return s;
}

void MasterPort::GetReadWrite(const IResource &res, bool *r, bool *w) {
  vector<IInsn *> insns = DesignUtil::GetInsnsByResource(&res);
  *r = false;
  *w = false;
  for (IInsn *insn : insns) {
    if (insn->GetOperand() == operand::kRead) {
      *r = true;
    } else if (insn->GetOperand() == operand::kWrite) {
      *w = true;
    } else {
      CHECK(false);
    }
  }
}
string MasterPort::LenPort() {
  return "axi_len" + PortSuffix();
}

}  // namespace axi
}  // namespace verilog
}  // namespace writer
}  // namespace iroha
<commit_msg>Sets burst len if specified.<commit_after>#include "writer/verilog/axi/master_port.h"

#include "design/design_util.h"
#include "iroha/insn_operands.h"
#include "iroha/i_design.h"
#include "iroha/logging.h"
#include "writer/module_template.h"
#include "writer/verilog/axi/master_controller.h"
#include "writer/verilog/embed.h"
#include "writer/verilog/insn_writer.h"
#include "writer/verilog/module.h"
#include "writer/verilog/ports.h"
#include "writer/verilog/shared_memory.h"
#include "writer/verilog/state.h"
#include "writer/verilog/table.h"

namespace iroha {
namespace writer {
namespace verilog {
namespace axi {

MasterPort::MasterPort(const IResource &res, const Table &table)
  : AxiPort(res, table) {
}

void MasterPort::BuildResource() {
  CHECK(tab_.GetITable() == res_.GetTable());
  string s = BuildPortToExt();
  BuildControllerInstance(s);
  if (!IsExclusiveAccessor()) {
    SharedMemory::BuildMemoryAccessorResource(*this, true, false,
					      res_.GetParentResource());
  }

  ostream &os = tmpl_->GetStream(kResourceSection);
  os << "  reg [31:0] " << AddrPort() << ";\n"
     << "  reg " << WenPort() << ";\n"
     << "  reg " << ReqPort() << ";\n"
     << "  wire " << AckPort() << ";\n";
  PortConfig cfg = AxiPort::GetPortConfig(res_);
  os << "  reg [" << (cfg.sram_addr_width - 1) << ":0] " << LenPort() << ";\n";

  ostream &is = tab_.InitialValueSectionStream();
  is << "      " << AddrPort() << " <= 0;\n"
     << "      " << WenPort() << " <= 0;\n"
     << "      " << ReqPort() << " <= 0;\n";

  map<IState *, IInsn *> accessors;
  CollectResourceCallers("*", &accessors);
  ostream &ss = tab_.StateOutputSectionStream();
  ss << "      " << ReqPort() << " <= ";
  if (accessors.size() == 0) {
    ss << "0;\n";
  } else {
    ss << JoinStatesWithSubState(accessors, 0) << ";\n";
  }
  map<IState *, IInsn *> writers;
  CollectResourceCallers("write", &writers);
  ss << "      " << WenPort() << " <= ";
  if (writers.size() == 0) {
    ss << "0;\n";
  } else {
    ss << JoinStatesWithSubState(writers, 0) << ";\n";
  }
}

void MasterPort::BuildInsn(IInsn *insn, State *st) {
    static const char I[] = "          ";
  string insn_st = InsnWriter::MultiCycleStateName(*(insn->GetResource()));
  ostream &os = st->StateBodySectionStream();
  os << I << "// AXI access request\n"
     << I << "if (" << insn_st << " == 0) begin\n"
     << I << "  " << AddrPort() << " <= "
     << InsnWriter::RegisterValue(*insn->inputs_[0], tab_.GetNames()) << ";\n";
  os << I << "  " << LenPort() << " <= ";
  if (insn->inputs_.size() >= 2) {
    os << InsnWriter::RegisterValue(*insn->inputs_[1], tab_.GetNames());
  } else {
    os << "~0";
  }
  os << ";\n";
  os << I << "  if (" << AckPort() << ") begin\n"
     << I << "    " << insn_st << " <= 3;\n"
     << I << "  end\n"
     << I << "end\n";
}

string MasterPort::ControllerName(const IResource &res, bool reset_polarity) {
  const IResource *mem_res = res.GetParentResource();
  IArray *array = mem_res->GetArray();
  int addr_width = array->GetAddressWidth();
  string s = "axi_master_controller_a" + Util::Itoa(addr_width);
  bool r, w;
  GetReadWrite(res, &r, &w);
  if (r) {
    s += "r";
  }
  if (w) {
    s += "w";
  }
  return s;
}

void MasterPort::WriteController(const IResource &res,
				 bool reset_polarity,
				 ostream &os) {
  MasterController c(res, reset_polarity);
  c.Write(os);
}

void MasterPort::BuildControllerInstance(const string &wires) {
  tab_.GetEmbeddedModules()->RequestAxiMasterController(&res_, reset_polarity_);
  ostream &es = tmpl_->GetStream(kEmbeddedInstanceSection);
  string name = ControllerName(res_, reset_polarity_);
  es << "  " << name << " inst_" << name
     << "(";
  OutputSRAMConnection(es);
  es << ", .addr(" << AddrPort() << "), "
     << ".wen(" << WenPort() << "), "
     << ".req(" << ReqPort() << "), "
     << ".len(" << LenPort() << "), "
     << ".ack(" << AckPort() << ") "
     << wires
     << ");\n";
}

string MasterPort::BuildPortToExt() {
  Module *mod = tab_.GetModule();
  string s;
  PortConfig cfg = AxiPort::GetPortConfig(res_);
  MasterController::AddPorts(cfg, mod, true, true, &s);
  for (mod = mod->GetParentModule(); mod != nullptr;
       mod = mod->GetParentModule()) {
    MasterController::AddPorts(cfg, mod, true, true, nullptr);
  }
  return s;
}

void MasterPort::GetReadWrite(const IResource &res, bool *r, bool *w) {
  vector<IInsn *> insns = DesignUtil::GetInsnsByResource(&res);
  *r = false;
  *w = false;
  for (IInsn *insn : insns) {
    if (insn->GetOperand() == operand::kRead) {
      *r = true;
    } else if (insn->GetOperand() == operand::kWrite) {
      *w = true;
    } else {
      CHECK(false);
    }
  }
}
string MasterPort::LenPort() {
  return "axi_len" + PortSuffix();
}

}  // namespace axi
}  // namespace verilog
}  // namespace writer
}  // namespace iroha
<|endoftext|>


<commit_before>/*
QWebSockets implements the WebSocket protocol as defined in RFC 6455.
Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)

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

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

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/*!
    \class QWebSocketDataProcessor
    The class QWebSocketDataProcessor is responsible for reading, validating and interpreting data from a websocket.
    It reads data from a QIODevice, validates it against RFC 6455, and parses it into frames (data, control).
    It emits signals that correspond to the type of the frame: textFrameReceived(), binaryFrameReceived(),
    textMessageReceived(), binaryMessageReceived(), pingReceived(), pongReceived() and closeReceived().
    Whenever an error is detected, the errorEncountered() signal is emitted.
    QWebSocketDataProcessor also checks if a frame is allowed in a sequence of frames (e.g. a continuation frame cannot follow a final frame).
    This class is an internal class used by QWebSocketInternal for data processing and validation.

    \sa Frame()

    \internal
*/
#include "qwebsocketdataprocessor_p.h"
#include "qwebsocketprotocol.h"
#include "qwebsocketframe_p.h"
#include <QtEndian>
#include <limits.h>
#include <QTextCodec>
#include <QTextDecoder>
#include <QDebug>

QT_BEGIN_NAMESPACE

/*!
    \internal
 */
QWebSocketDataProcessor::QWebSocketDataProcessor(QObject *parent) :
    QObject(parent),
    m_processingState(PS_READ_HEADER),
    m_isFinalFrame(false),
    m_isFragmented(false),
    m_opCode(QWebSocketProtocol::OC_CLOSE),
    m_isControlFrame(false),
    m_hasMask(false),
    m_mask(0),
    m_binaryMessage(),
    m_textMessage(),
    m_payloadLength(0),
    m_pConverterState(0),
    m_pTextCodec(QTextCodec::codecForName("UTF-8"))
{
    clear();
}

/*!
    \internal
 */
QWebSocketDataProcessor::~QWebSocketDataProcessor()
{
    clear();
    if (m_pConverterState)
    {
        delete m_pConverterState;
        m_pConverterState = 0;
    }
}

/*!
    \internal
 */
quint64 QWebSocketDataProcessor::maxMessageSize()
{
    return MAX_MESSAGE_SIZE_IN_BYTES;
}

/*!
    \internal
 */
quint64 QWebSocketDataProcessor::maxFrameSize()
{
    return MAX_FRAME_SIZE_IN_BYTES;
}

/*!
    \internal
 */
void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
{
    bool isDone = false;

    while (!isDone)
    {
        QWebSocketFrame frame = QWebSocketFrame::readFrame(pIoDevice);
        if (frame.isValid())
        {
            if (frame.isControlFrame())
            {
                isDone = processControlFrame(frame);
            }
            else    //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
            {
                if (!m_isFragmented && frame.isContinuationFrame())
                {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("Received Continuation frame, while there is nothing to continue."));
                    return;
                }
                if (m_isFragmented && frame.isDataFrame() && !frame.isContinuationFrame())
                {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("All data frames after the initial data frame must have opcode 0 (continuation)."));
                    return;
                }
                if (!frame.isContinuationFrame())
                {
                    m_opCode = frame.getOpCode();
                    m_isFragmented = !frame.isFinalFrame();
                }
                quint64 messageLength = (quint64)(m_opCode == QWebSocketProtocol::OC_TEXT) ? m_textMessage.length() : m_binaryMessage.length();
                if ((messageLength + quint64(frame.getPayload().length())) > MAX_MESSAGE_SIZE_IN_BYTES)
                {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CC_TOO_MUCH_DATA, tr("Received message is too big."));
                    return;
                }

                if (m_opCode == QWebSocketProtocol::OC_TEXT)
                {
                    QString frameTxt = m_pTextCodec->toUnicode(frame.getPayload().constData(), frame.getPayload().size(), m_pConverterState);
                    bool failed = (m_pConverterState->invalidChars != 0) || (frame.isFinalFrame() && (m_pConverterState->remainingChars != 0));
                    if (failed)
                    {
                        clear();
                        Q_EMIT errorEncountered(QWebSocketProtocol::CC_WRONG_DATATYPE, tr("Invalid UTF-8 code encountered."));
                        return;
                    }
                    else
                    {
                        m_textMessage.append(frameTxt);
                        Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
                    }
                }
                else
                {
                    m_binaryMessage.append(frame.getPayload());
                    Q_EMIT binaryFrameReceived(frame.getPayload(), frame.isFinalFrame());
                }

                if (frame.isFinalFrame())
                {
                    if (m_opCode == QWebSocketProtocol::OC_TEXT)
                    {
                        Q_EMIT textMessageReceived(m_textMessage);
                    }
                    else
                    {
                        Q_EMIT binaryMessageReceived(m_binaryMessage);
                    }
                    clear();
                    isDone = true;
                }
            }
        }
        else
        {
            Q_EMIT errorEncountered(frame.getCloseCode(), frame.getCloseReason());
            clear();
            isDone = true;
        }
    }
}

/*!
    \internal
 */
void QWebSocketDataProcessor::clear()
{
    m_processingState = PS_READ_HEADER;
    m_isFinalFrame = false;
    m_isFragmented = false;
    m_opCode = QWebSocketProtocol::OC_CLOSE;
    m_hasMask = false;
    m_mask = 0;
    m_binaryMessage.clear();
    m_textMessage.clear();
    m_payloadLength = 0;
    if (m_pConverterState)
    {
        if ((m_pConverterState->remainingChars != 0) || (m_pConverterState->invalidChars != 0))
        {
            delete m_pConverterState;
            m_pConverterState = 0;
        }
    }
    if (!m_pConverterState)
    {
        m_pConverterState = new QTextCodec::ConverterState(QTextCodec::ConvertInvalidToNull | QTextCodec::IgnoreHeader);
    }
}

/*!
    \internal
 */
bool QWebSocketDataProcessor::processControlFrame(const QWebSocketFrame &frame)
{
    bool mustStopProcessing = false;
    switch (frame.getOpCode())
    {
    case QWebSocketProtocol::OC_PING:
    {
        Q_EMIT pingReceived(frame.getPayload());
        break;
    }
    case QWebSocketProtocol::OC_PONG:
    {
        Q_EMIT pongReceived(frame.getPayload());
        break;
    }
    case QWebSocketProtocol::OC_CLOSE:
    {
        quint16 closeCode = QWebSocketProtocol::CC_NORMAL;
        QString closeReason;
        QByteArray payload = frame.getPayload();
        if (payload.size() == 1)
        {
            closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
            closeReason = tr("Payload of close frame is too small.");
        }
        else if (payload.size() > 1)   //close frame can have a close code and reason
        {
            closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(payload.constData()));
            if (!QWebSocketProtocol::isCloseCodeValid(closeCode))
            {
                closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
                closeReason = tr("Invalid close code %1 detected.").arg(closeCode);
            }
            else
            {
                if (payload.size() > 2)
                {
                    QTextCodec *tc = QTextCodec::codecForName("UTF-8");
                    QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
                    closeReason = tc->toUnicode(payload.constData() + 2, payload.size() - 2, &state);
                    bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
                    if (failed)
                    {
                        closeCode = QWebSocketProtocol::CC_WRONG_DATATYPE;
                        closeReason = tr("Invalid UTF-8 code encountered.");
                    }
                }
            }
        }
        mustStopProcessing = true;
        Q_EMIT closeReceived(static_cast<QWebSocketProtocol::CloseCode>(closeCode), closeReason);
        break;
    }
    case QWebSocketProtocol::OC_CONTINUE:
    case QWebSocketProtocol::OC_BINARY:
    case QWebSocketProtocol::OC_TEXT:
    case QWebSocketProtocol::OC_RESERVED_3:
    case QWebSocketProtocol::OC_RESERVED_4:
    case QWebSocketProtocol::OC_RESERVED_5:
    case QWebSocketProtocol::OC_RESERVED_6:
    case QWebSocketProtocol::OC_RESERVED_7:
    case QWebSocketProtocol::OC_RESERVED_C:
    case QWebSocketProtocol::OC_RESERVED_B:
    case QWebSocketProtocol::OC_RESERVED_D:
    case QWebSocketProtocol::OC_RESERVED_E:
    case QWebSocketProtocol::OC_RESERVED_F:
    {
        //do nothing
        //case added to make C++ compiler happy
        break;
    }
    default:
    {
        qDebug() << "DataProcessor::processControlFrame: Invalid opcode detected:" << static_cast<int>(frame.getOpCode());
        //Do nothing
        break;
    }
    }
    return mustStopProcessing;
}

QT_END_NAMESPACE
<commit_msg>Added clarifying comment<commit_after>/*
QWebSockets implements the WebSocket protocol as defined in RFC 6455.
Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)

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

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

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/*!
    \class QWebSocketDataProcessor
    The class QWebSocketDataProcessor is responsible for reading, validating and interpreting data from a websocket.
    It reads data from a QIODevice, validates it against RFC 6455, and parses it into frames (data, control).
    It emits signals that correspond to the type of the frame: textFrameReceived(), binaryFrameReceived(),
    textMessageReceived(), binaryMessageReceived(), pingReceived(), pongReceived() and closeReceived().
    Whenever an error is detected, the errorEncountered() signal is emitted.
    QWebSocketDataProcessor also checks if a frame is allowed in a sequence of frames (e.g. a continuation frame cannot follow a final frame).
    This class is an internal class used by QWebSocketInternal for data processing and validation.

    \sa Frame()

    \internal
*/
#include "qwebsocketdataprocessor_p.h"
#include "qwebsocketprotocol.h"
#include "qwebsocketframe_p.h"
#include <QtEndian>
#include <limits.h>
#include <QTextCodec>
#include <QTextDecoder>
#include <QDebug>

QT_BEGIN_NAMESPACE

/*!
    \internal
 */
QWebSocketDataProcessor::QWebSocketDataProcessor(QObject *parent) :
    QObject(parent),
    m_processingState(PS_READ_HEADER),
    m_isFinalFrame(false),
    m_isFragmented(false),
    m_opCode(QWebSocketProtocol::OC_CLOSE),
    m_isControlFrame(false),
    m_hasMask(false),
    m_mask(0),
    m_binaryMessage(),
    m_textMessage(),
    m_payloadLength(0),
    m_pConverterState(0),
    m_pTextCodec(QTextCodec::codecForName("UTF-8"))
{
    clear();
}

/*!
    \internal
 */
QWebSocketDataProcessor::~QWebSocketDataProcessor()
{
    clear();
    if (m_pConverterState)
    {
        delete m_pConverterState;
        m_pConverterState = 0;
    }
}

/*!
    \internal
 */
quint64 QWebSocketDataProcessor::maxMessageSize()
{
    return MAX_MESSAGE_SIZE_IN_BYTES;
}

/*!
    \internal
 */
quint64 QWebSocketDataProcessor::maxFrameSize()
{
    return MAX_FRAME_SIZE_IN_BYTES;
}

/*!
    \internal
 */
void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
{
    bool isDone = false;

    while (!isDone)
    {
        QWebSocketFrame frame = QWebSocketFrame::readFrame(pIoDevice);
        if (frame.isValid())
        {
            if (frame.isControlFrame())
            {
                isDone = processControlFrame(frame);
            }
            else    //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
            {
                if (!m_isFragmented && frame.isContinuationFrame())
                {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("Received Continuation frame, while there is nothing to continue."));
                    return;
                }
                if (m_isFragmented && frame.isDataFrame() && !frame.isContinuationFrame())
                {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("All data frames after the initial data frame must have opcode 0 (continuation)."));
                    return;
                }
                if (!frame.isContinuationFrame())
                {
                    m_opCode = frame.getOpCode();
                    m_isFragmented = !frame.isFinalFrame();
                }
                quint64 messageLength = (quint64)(m_opCode == QWebSocketProtocol::OC_TEXT) ? m_textMessage.length() : m_binaryMessage.length();
                if ((messageLength + quint64(frame.getPayload().length())) > MAX_MESSAGE_SIZE_IN_BYTES)
                {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CC_TOO_MUCH_DATA, tr("Received message is too big."));
                    return;
                }

                if (m_opCode == QWebSocketProtocol::OC_TEXT)
                {
                    QString frameTxt = m_pTextCodec->toUnicode(frame.getPayload().constData(), frame.getPayload().size(), m_pConverterState);
                    bool failed = (m_pConverterState->invalidChars != 0) || (frame.isFinalFrame() && (m_pConverterState->remainingChars != 0));
                    if (failed)
                    {
                        clear();
                        Q_EMIT errorEncountered(QWebSocketProtocol::CC_WRONG_DATATYPE, tr("Invalid UTF-8 code encountered."));
                        return;
                    }
                    else
                    {
                        m_textMessage.append(frameTxt);
                        Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
                    }
                }
                else
                {
                    m_binaryMessage.append(frame.getPayload());
                    Q_EMIT binaryFrameReceived(frame.getPayload(), frame.isFinalFrame());
                }

                if (frame.isFinalFrame())
                {
                    if (m_opCode == QWebSocketProtocol::OC_TEXT)
                    {
                        Q_EMIT textMessageReceived(m_textMessage);
                    }
                    else
                    {
                        Q_EMIT binaryMessageReceived(m_binaryMessage);
                    }
                    clear();
                    isDone = true;
                }
            }
        }
        else
        {
            Q_EMIT errorEncountered(frame.getCloseCode(), frame.getCloseReason());
            clear();
            isDone = true;
        }
    }
}

/*!
    \internal
 */
void QWebSocketDataProcessor::clear()
{
    m_processingState = PS_READ_HEADER;
    m_isFinalFrame = false;
    m_isFragmented = false;
    m_opCode = QWebSocketProtocol::OC_CLOSE;
    m_hasMask = false;
    m_mask = 0;
    m_binaryMessage.clear();
    m_textMessage.clear();
    m_payloadLength = 0;
    if (m_pConverterState)
    {
        if ((m_pConverterState->remainingChars != 0) || (m_pConverterState->invalidChars != 0))
        {
            delete m_pConverterState;
            m_pConverterState = 0;
        }
    }
    if (!m_pConverterState)
    {
        m_pConverterState = new QTextCodec::ConverterState(QTextCodec::ConvertInvalidToNull | QTextCodec::IgnoreHeader);
    }
}

/*!
    \internal
 */
bool QWebSocketDataProcessor::processControlFrame(const QWebSocketFrame &frame)
{
    bool mustStopProcessing = false;
    switch (frame.getOpCode())
    {
    case QWebSocketProtocol::OC_PING:
    {
        Q_EMIT pingReceived(frame.getPayload());
        break;
    }
    case QWebSocketProtocol::OC_PONG:
    {
        Q_EMIT pongReceived(frame.getPayload());
        break;
    }
    case QWebSocketProtocol::OC_CLOSE:
    {
        quint16 closeCode = QWebSocketProtocol::CC_NORMAL;
        QString closeReason;
        QByteArray payload = frame.getPayload();
        if (payload.size() == 1)    //size is either 0 (no close code and no reason) or >= 2 (at least a close code of 2 bytes)
        {
            closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
            closeReason = tr("Payload of close frame is too small.");
        }
        else if (payload.size() > 1)   //close frame can have a close code and reason
        {
            closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(payload.constData()));
            if (!QWebSocketProtocol::isCloseCodeValid(closeCode))
            {
                closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
                closeReason = tr("Invalid close code %1 detected.").arg(closeCode);
            }
            else
            {
                if (payload.size() > 2)
                {
                    QTextCodec *tc = QTextCodec::codecForName("UTF-8");
                    QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
                    closeReason = tc->toUnicode(payload.constData() + 2, payload.size() - 2, &state);
                    bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
                    if (failed)
                    {
                        closeCode = QWebSocketProtocol::CC_WRONG_DATATYPE;
                        closeReason = tr("Invalid UTF-8 code encountered.");
                    }
                }
            }
        }
        mustStopProcessing = true;
        Q_EMIT closeReceived(static_cast<QWebSocketProtocol::CloseCode>(closeCode), closeReason);
        break;
    }
    case QWebSocketProtocol::OC_CONTINUE:
    case QWebSocketProtocol::OC_BINARY:
    case QWebSocketProtocol::OC_TEXT:
    case QWebSocketProtocol::OC_RESERVED_3:
    case QWebSocketProtocol::OC_RESERVED_4:
    case QWebSocketProtocol::OC_RESERVED_5:
    case QWebSocketProtocol::OC_RESERVED_6:
    case QWebSocketProtocol::OC_RESERVED_7:
    case QWebSocketProtocol::OC_RESERVED_C:
    case QWebSocketProtocol::OC_RESERVED_B:
    case QWebSocketProtocol::OC_RESERVED_D:
    case QWebSocketProtocol::OC_RESERVED_E:
    case QWebSocketProtocol::OC_RESERVED_F:
    {
        //do nothing
        //case added to make C++ compiler happy
        break;
    }
    default:
    {
        qDebug() << "DataProcessor::processControlFrame: Invalid opcode detected:" << static_cast<int>(frame.getOpCode());
        //Do nothing
        break;
    }
    }
    return mustStopProcessing;
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Mohammad Mehdi Saboorian
 *
 * This is part of moor, a wrapper for libarchive
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "archive_writer.hpp"
#include "archive_entry.hpp"
#include "archive_read_disk.hpp"
#include "memory_writer_callback.hpp"

#include <archive.h>
#include <archive_entry.h>

#include <fstream>
#include <stdexcept>
#include <system_error>

#include <sys/types.h>
#include <sys/stat.h>

int moor::ArchiveWriter::openCallbackWrapper(archive*, void* ud)
{
    WriterCallbackData* wcb = reinterpret_cast<WriterCallbackData*>(ud);
    return wcb->m_open(wcb->m_writer, wcb->m_userData);
}

ssize_t moor::ArchiveWriter::writeCallbackWrapper(archive*,
                                                  void* ud,
                                                  const void* buffer,
                                                  size_t size)
{
    WriterCallbackData* wcb = reinterpret_cast<WriterCallbackData*>(ud);
    return wcb->m_write(wcb->m_writer, wcb->m_userData, buffer, size);
}

int moor::ArchiveWriter::closeCallbackWrapper(archive*, void* ud)
{
    WriterCallbackData* wcb = reinterpret_cast<WriterCallbackData*>(ud);
    return wcb->m_close(wcb->m_writer, wcb->m_userData);
}

moor::ArchiveWriter::ArchiveWriter(const std::string& archive_file_name_,
                                   const moor::Format format_,
                                   const moor::Filter filter_)
    : Archive(archive_write_new(), archive_file_name_),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive compression
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);
    checkError(openFilename(cfilename()), true);
}

moor::ArchiveWriter::ArchiveWriter(std::vector<unsigned char>& out_buffer_,
                                   const moor::Format format_,
                                   const moor::Filter filter_)
    : Archive(archive_write_new()),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive filter
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);
    checkError(openMemory(out_buffer_), true);
}

moor::ArchiveWriter::ArchiveWriter(unsigned char* out_buffer_,
                                   size_t* size_,
                                   const moor::Format format_,
                                   const moor::Filter filter_)
    : Archive(archive_write_new()),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(nullptr),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive filter
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);
    checkError(openMemory(out_buffer_, size_), true);
}

moor::ArchiveWriter::ArchiveWriter(OpenCallback openCB,
                                   WriteCallback writeCB,
                                   CloseCallback closeCB,
                                   const moor::Format format_,
                                   const moor::Filter filter_,
                                   void* userData)
    : Archive(archive_write_new()),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_buffer(new char[bufferSize()]),
      m_callbackData(WriterCallbackData::create(*this,
                                                openCB,
                                                writeCB,
                                                closeCB,
                                                userData)),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive filter
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);

    int r = archive_write_open(m_archive,
                               m_callbackData.get(),
                               ArchiveWriter::openCallbackWrapper,
                               ArchiveWriter::writeCallbackWrapper,
                               ArchiveWriter::closeCallbackWrapper);
    checkError(r);
}

moor::ArchiveWriter::~ArchiveWriter()
{
    close();
}

int moor::ArchiveWriter::writeHeader(archive_entry* e)
{
    return archive_write_header(m_archive, e);
}

int moor::ArchiveWriter::openFilename(const char* path)
{
    return archive_write_open_filename(m_archive, path);
}

int moor::ArchiveWriter::openMemory(std::vector<unsigned char>& outBuf)
{
    return write_open_memory(m_archive, &outBuf);
}

int moor::ArchiveWriter::openMemory(void* buf, size_t* bufSize)
{
    return archive_write_open_memory(m_archive, buf, *bufSize, bufSize);
}

void moor::ArchiveWriter::addHeader(const std::string& entry_name_,
                                    const FileType entry_type_,
                                    const long long size_,
                                    const int permission_)
{
    m_entry.clear();
    m_entry.set_pathname(entry_name_.c_str());
    m_entry.set_perm(permission_);
    m_entry.set_filetype(entry_type_);
    m_entry.set_size(size_);
    checkError(writeHeader(m_entry));
}

void moor::ArchiveWriter::addHeader(const std::string& filePath,
                                    const struct stat* statBuf)
{
    ArchiveReadDisk disk;

    m_entry.clear();
    m_entry.set_pathname(filePath.c_str());
    checkError(disk.entryFromFile(m_entry, -1, statBuf));
    checkError(writeHeader(m_entry));
}

void moor::ArchiveWriter::addContent(const char b)
{
    archive_write_data(m_archive, &b, 1);
}

void moor::ArchiveWriter::addContent(const void* data, const unsigned long long size)
{
    archive_write_data(m_archive, data, size);
}

void moor::ArchiveWriter::addFinish()
{
    archive_write_finish_entry(m_archive);
}

ssize_t moor::ArchiveWriter::writeData(const void* buf, size_t bufSize)
{
    return archive_write_data(m_archive, buf, bufSize);
}

void moor::ArchiveWriter::writeFileData(const char* path)
{
    std::ifstream file(path, std::ios::in);

    while (file.good())
    {
        file.read(m_buffer.get(), bufferSize());
        writeData(m_buffer.get(), static_cast<size_t>(file.gcount()));
    }

#if 0
    int fd = open(archive_entry_sourcepath(m_entry), O_RDONLY);
    auto len = read(fd, buf, sizeof(buf));

    while (len > 0)
    {
        archive_write_data(m_archive, buf, len);
        len = read(fd, buf, sizeof(buf));
    }

    ::close(fd);
#endif
}

void moor::ArchiveWriter::addFile(const std::string& file_path)
{
    struct stat file_stat;

    if (stat(file_path.c_str(), &file_stat) < 0)
    {
        throw std::system_error(std::error_code(errno, std::generic_category()));
    }

    addHeader(file_path, &file_stat);

    if (!S_ISREG(file_stat.st_mode))
    {
        throw std::system_error(std::make_error_code(std::errc::not_supported));
    }

    writeFileData(file_path.c_str());
    addFinish();
}

void moor::ArchiveWriter::addFile(const std::string& entry_name,
                                  const void* data,
                                  unsigned long long size)
{
    addHeader(entry_name, FileType::Regular, size);
    addContent(data, size);
    addFinish();
}

void moor::ArchiveWriter::addDiskPath(const std::string& path, ArchiveMatch* match)
{
    moor::ArchiveReadDisk disk;

    checkError(disk.open(path.c_str()), true);

    if (match)
    {
        disk.checkError(disk.setMatchFilter(*match), true);
    }

    while (true)
    {
        int r = disk.nextHeader2(m_entry);
        if (r == ARCHIVE_EOF)
        {
            break;
        }

        disk.checkError(r, true);
        disk.descend();

        r = writeHeader(m_entry);
        checkError(r, true);

        writeFileData(m_entry.sourcepath());
    }
}

void moor::ArchiveWriter::addDirectory(const std::string& directory_name)
{
    addHeader(directory_name, FileType::Directory, 0777);
    addFinish();
}

void moor::ArchiveWriter::close()
{
    if (m_open)
    {
        m_open = false;

        if (m_archive)
        {
            archive_write_close(m_archive);
            archive_write_free(m_archive);
        }

        if (m_entry)
        {
            archive_entry_free(m_entry);
        }
    }
}
<commit_msg>Fix -Wreorder<commit_after>/*
 * Copyright (c) 2013 Mohammad Mehdi Saboorian
 *
 * This is part of moor, a wrapper for libarchive
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "archive_writer.hpp"
#include "archive_entry.hpp"
#include "archive_read_disk.hpp"
#include "memory_writer_callback.hpp"

#include <archive.h>
#include <archive_entry.h>

#include <fstream>
#include <stdexcept>
#include <system_error>

#include <sys/types.h>
#include <sys/stat.h>

int moor::ArchiveWriter::openCallbackWrapper(archive*, void* ud)
{
    WriterCallbackData* wcb = reinterpret_cast<WriterCallbackData*>(ud);
    return wcb->m_open(wcb->m_writer, wcb->m_userData);
}

ssize_t moor::ArchiveWriter::writeCallbackWrapper(archive*,
                                                  void* ud,
                                                  const void* buffer,
                                                  size_t size)
{
    WriterCallbackData* wcb = reinterpret_cast<WriterCallbackData*>(ud);
    return wcb->m_write(wcb->m_writer, wcb->m_userData, buffer, size);
}

int moor::ArchiveWriter::closeCallbackWrapper(archive*, void* ud)
{
    WriterCallbackData* wcb = reinterpret_cast<WriterCallbackData*>(ud);
    return wcb->m_close(wcb->m_writer, wcb->m_userData);
}

moor::ArchiveWriter::ArchiveWriter(const std::string& archive_file_name_,
                                   const moor::Format format_,
                                   const moor::Filter filter_)
    : Archive(archive_write_new(), archive_file_name_),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive compression
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);
    checkError(openFilename(cfilename()), true);
}

moor::ArchiveWriter::ArchiveWriter(std::vector<unsigned char>& out_buffer_,
                                   const moor::Format format_,
                                   const moor::Filter filter_)
    : Archive(archive_write_new()),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive filter
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);
    checkError(openMemory(out_buffer_), true);
}

moor::ArchiveWriter::ArchiveWriter(unsigned char* out_buffer_,
                                   size_t* size_,
                                   const moor::Format format_,
                                   const moor::Filter filter_)
    : Archive(archive_write_new()),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(nullptr),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive filter
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);
    checkError(openMemory(out_buffer_, size_), true);
}

moor::ArchiveWriter::ArchiveWriter(OpenCallback openCB,
                                   WriteCallback writeCB,
                                   CloseCallback closeCB,
                                   const moor::Format format_,
                                   const moor::Filter filter_,
                                   void* userData)
    : Archive(archive_write_new()),
      m_entry(m_archive, archive_entry_new()),
      m_format(format_),
      m_filter(filter_),
      m_callbackData(WriterCallbackData::create(*this,
                                                openCB,
                                                writeCB,
                                                closeCB,
                                                userData)),
      m_buffer(new char[bufferSize()]),
      m_open(true)
{
    // Set archive format
    checkError(archive_write_set_format(m_archive, static_cast<int>(m_format)), true);

    // Set archive filter
    checkError(archive_write_add_filter(m_archive, static_cast<int>(m_filter)), true);

    int r = archive_write_open(m_archive,
                               m_callbackData.get(),
                               ArchiveWriter::openCallbackWrapper,
                               ArchiveWriter::writeCallbackWrapper,
                               ArchiveWriter::closeCallbackWrapper);
    checkError(r);
}

moor::ArchiveWriter::~ArchiveWriter()
{
    close();
}

int moor::ArchiveWriter::writeHeader(archive_entry* e)
{
    return archive_write_header(m_archive, e);
}

int moor::ArchiveWriter::openFilename(const char* path)
{
    return archive_write_open_filename(m_archive, path);
}

int moor::ArchiveWriter::openMemory(std::vector<unsigned char>& outBuf)
{
    return write_open_memory(m_archive, &outBuf);
}

int moor::ArchiveWriter::openMemory(void* buf, size_t* bufSize)
{
    return archive_write_open_memory(m_archive, buf, *bufSize, bufSize);
}

void moor::ArchiveWriter::addHeader(const std::string& entry_name_,
                                    const FileType entry_type_,
                                    const long long size_,
                                    const int permission_)
{
    m_entry.clear();
    m_entry.set_pathname(entry_name_.c_str());
    m_entry.set_perm(permission_);
    m_entry.set_filetype(entry_type_);
    m_entry.set_size(size_);
    checkError(writeHeader(m_entry));
}

void moor::ArchiveWriter::addHeader(const std::string& filePath,
                                    const struct stat* statBuf)
{
    ArchiveReadDisk disk;

    m_entry.clear();
    m_entry.set_pathname(filePath.c_str());
    checkError(disk.entryFromFile(m_entry, -1, statBuf));
    checkError(writeHeader(m_entry));
}

void moor::ArchiveWriter::addContent(const char b)
{
    archive_write_data(m_archive, &b, 1);
}

void moor::ArchiveWriter::addContent(const void* data, const unsigned long long size)
{
    archive_write_data(m_archive, data, size);
}

void moor::ArchiveWriter::addFinish()
{
    archive_write_finish_entry(m_archive);
}

ssize_t moor::ArchiveWriter::writeData(const void* buf, size_t bufSize)
{
    return archive_write_data(m_archive, buf, bufSize);
}

void moor::ArchiveWriter::writeFileData(const char* path)
{
    std::ifstream file(path, std::ios::in);

    while (file.good())
    {
        file.read(m_buffer.get(), bufferSize());
        writeData(m_buffer.get(), static_cast<size_t>(file.gcount()));
    }

#if 0
    int fd = open(archive_entry_sourcepath(m_entry), O_RDONLY);
    auto len = read(fd, buf, sizeof(buf));

    while (len > 0)
    {
        archive_write_data(m_archive, buf, len);
        len = read(fd, buf, sizeof(buf));
    }

    ::close(fd);
#endif
}

void moor::ArchiveWriter::addFile(const std::string& file_path)
{
    struct stat file_stat;

    if (stat(file_path.c_str(), &file_stat) < 0)
    {
        throw std::system_error(std::error_code(errno, std::generic_category()));
    }

    addHeader(file_path, &file_stat);

    if (!S_ISREG(file_stat.st_mode))
    {
        throw std::system_error(std::make_error_code(std::errc::not_supported));
    }

    writeFileData(file_path.c_str());
    addFinish();
}

void moor::ArchiveWriter::addFile(const std::string& entry_name,
                                  const void* data,
                                  unsigned long long size)
{
    addHeader(entry_name, FileType::Regular, size);
    addContent(data, size);
    addFinish();
}

void moor::ArchiveWriter::addDiskPath(const std::string& path, ArchiveMatch* match)
{
    moor::ArchiveReadDisk disk;

    checkError(disk.open(path.c_str()), true);

    if (match)
    {
        disk.checkError(disk.setMatchFilter(*match), true);
    }

    while (true)
    {
        int r = disk.nextHeader2(m_entry);
        if (r == ARCHIVE_EOF)
        {
            break;
        }

        disk.checkError(r, true);
        disk.descend();

        r = writeHeader(m_entry);
        checkError(r, true);

        writeFileData(m_entry.sourcepath());
    }
}

void moor::ArchiveWriter::addDirectory(const std::string& directory_name)
{
    addHeader(directory_name, FileType::Directory, 0777);
    addFinish();
}

void moor::ArchiveWriter::close()
{
    if (m_open)
    {
        m_open = false;

        if (m_archive)
        {
            archive_write_close(m_archive);
            archive_write_free(m_archive);
        }

        if (m_entry)
        {
            archive_entry_free(m_entry);
        }
    }
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_OPENCL_KERNELS_MATRIX_MULTIPLY_HPP
#define STAN_MATH_OPENCL_KERNELS_MATRIX_MULTIPLY_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_cl.hpp>
#include <stan/math/opencl/buffer_types.hpp>

namespace stan {
namespace math {
namespace opencl_kernels {
// \cond
static const char* matrix_multiply_kernel_code = STRINGIFY(
    // \endcond
    /**
     * Matrix multiplication on the OpenCL device
     *
     * @param[in] A the left matrix in matrix multiplication
     * @param[in] B the right matrix in matrix multiplication
     * @param[out] C the output matrix
     * @param[in] M Number of rows for matrix A
     * @param[in] N Number of cols for matrix B
     * @param[in] K Number of cols for matrix A and number of rows for matrix B
     * @param[in] lower_upper_A the triangularity of A (lower, upper or none)
     * @param[in] lower_upper_B the triangularity of B (lower, upper or none)
     */
    __kernel void matrix_multiply(
        const __global double* A, const __global double* B, __global double* C,
        const int M, const int N, const int K, unsigned int lower_upper_A,
        unsigned int lower_upper_B) {
      // thread index inside the thread_block
      const int thread_block_row = get_local_id(0);
      const int thread_block_col = get_local_id(1);
      // global thread index
      const int i = THREAD_BLOCK_SIZE * get_group_id(0) + thread_block_row;
      const int j = THREAD_BLOCK_SIZE * get_group_id(1) + thread_block_col;
      // identify if the matrix multiply is split
      const int split_id = get_global_id(2);
      const int split_size = get_global_size(2);
      // local memory
      __local double A_local[THREAD_BLOCK_SIZE][THREAD_BLOCK_SIZE];
      __local double B_local[THREAD_BLOCK_SIZE][THREAD_BLOCK_SIZE];

      double acc[WORK_PER_THREAD];
      for (int w = 0; w < WORK_PER_THREAD; w++) {
        acc[w] = 0.0;
      }
      // the number of tiles for each scalar product in the matrix mulitply
      const int num_tiles = (K + THREAD_BLOCK_SIZE - 1) / THREAD_BLOCK_SIZE;
      // in case of splitting the matrix multiply we need
      // use split_offset_tiles the threads assigned part
      // of the scalar products, while the split_tiles
      // determines the number of tiles a thread multiplies
      // if split_size = 1, each thread calculates the
      // the entire scalar product for all assigned
      // elements of the resulting matrix, meaning that
      // split_offset_tiles is 0 and split_tiles = num_tiles
      int split_tiles = num_tiles / split_size;
      const int split_remainder = num_tiles % split_size;
      int split_offset_tiles = split_id * split_tiles;
      if (split_id < split_remainder) {
        split_offset_tiles = split_offset_tiles + split_id;
        split_tiles++;
      } else {
        split_offset_tiles = split_offset_tiles + split_remainder;
      }
      // This kernel is based on the well known
      // general matrix multiplication kernels that
      // use tiling for shared memory
      // In cases where a matrix is lower triangular
      // its not necessary to multiply the elements
      // over the diagonal, therefore those tiles
      // in the matrix multiply can be skipped.
      // With upper triangular matrices we dont need
      // to multiply the elements under the diagonal,
      // so those tiles can be skipped.
      // The following code determines the start and
      // end tile based on triangularity of the input matrices
      // If no matrices are triangular the starting tile
      // is 0 and the end tile is num_tiles-1 which
      // is then a general matrix multiply
      const int end_tile_A
          = lower_upper_A == LOWER ? (i / THREAD_BLOCK_SIZE) : (num_tiles - 1);
      const int end_tile_B
          = lower_upper_B == UPPER ? (j / THREAD_BLOCK_SIZE) : (num_tiles - 1);
      const int start_tile_A
          = lower_upper_A == UPPER ? (i / THREAD_BLOCK_SIZE) : 0;
      const int start_tile_B
          = lower_upper_B == LOWER ? (j / THREAD_BLOCK_SIZE) : 0;
      // the starting and end tiles for a thread are determined by
      // split_offset_tiles and split_tiles. If the input matrix is
      // triangular some tiles can be skipped in which case we
      // either start the scalar product at larger cols/rows
      // or end them at smaller cols/rows.
      int start_tile = max(start_tile_A, start_tile_B);
      start_tile = max(start_tile, split_offset_tiles);
      int end_tile = min(end_tile_A, end_tile_B);                      // NOLINT
      end_tile = min(end_tile, split_offset_tiles + split_tiles - 1);  // NOLINT
      for (int tile_idx = start_tile; tile_idx <= end_tile; tile_idx++) {
        const int tiled_i = THREAD_BLOCK_SIZE * tile_idx + thread_block_row;
        const int tiled_j = THREAD_BLOCK_SIZE * tile_idx + thread_block_col;
        // each thread copies WORK_PER_THREAD values to the local
        // memory
        for (int w = 0; w < WORK_PER_THREAD; w++) {
          // For the tiles on the diagonal we can ignore the values over
          // the diagonal if the matrix is lower triangular or under
          // the diagonal if the matrix is upper triangular
          const A_curr_j = tiled_j + w * THREAD_BLOCK_SIZE_COL;
          const B_curr_j = j + w * THREAD_BLOCK_SIZE_COL;
          // check if the indexes are outside the matrix
          // or under/above the diagonal with upper/lower
          // triangular matrices
          if (A_curr_j >= K || i >= M
              || (lower_upper_A == LOWER && A_curr_j > i)
              || (lower_upper_A == UPPER && A_curr_j < i)) {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = 0.0;
          } else {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = A[A_curr_j * M + i];
          }
          if (B_curr_j >= N || tiled_i >= K
              || (lower_upper_B == LOWER && B_curr_j > tiled_i)
              || (lower_upper_B == UPPER && B_curr_j < tiled_i)) {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = 0.0;
          } else {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = B[B_curr_j * K + tiled_i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        for (int block_idx = 0; block_idx < THREAD_BLOCK_SIZE; block_idx++) {
          for (int w = 0; w < WORK_PER_THREAD; w++) {
            acc[w] += A_local[block_idx][thread_block_row]
                      * B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                               [block_idx];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
      }
      // each thread saves WORK_PER_THREAD values
      for (int w = 0; w < WORK_PER_THREAD; w++) {
        // This prevents threads from accessing elements
        // outside the allocated memory for C. The check
        // is in the loop because some threads
        // can be assigned elements in and out of
        // the allocated memory.
        if ((j + w * THREAD_BLOCK_SIZE_COL) < N && i < M) {
          C[split_id * M * N + (j + w * THREAD_BLOCK_SIZE_COL) * M + i]
              = acc[w];
        }
      }
    }
    // \cond
);
// \endcond

/**
 * See the docs for \link kernels/matrix_multiply.hpp matrix_multiply() \endlink
 */
const kernel_cl<in_buffer, in_buffer, out_buffer, int, int, int,
                TriangularViewCL, TriangularViewCL>
    matrix_multiply("matrix_multiply",
                    {thread_block_helpers, matrix_multiply_kernel_code},
                    {{"THREAD_BLOCK_SIZE", 32}, {"WORK_PER_THREAD", 8}});

// \cond
static const char* matrix_vector_multiply_kernel_code = STRINGIFY(
    // \endcond
    /**
     * Matrix-vector multiplication R=A*B on the OpenCL device
     *
     * @param[in] A matrix in matrix-vector multiplication
     * @param[in] B vector in matrix-vector multiplication
     * @param[out] R the output vector
     * @param[in] M Number of rows for matrix A
     * @param[in] N Number of cols for matrix A and number of rows for vector B
     * @param[in] lower_upper_A the triangularity of A (lower, upper or none)
     * @param[in] lower_upper_B the triangularity of B (lower, upper or none)
     */
    __kernel void matrix_vector_multiply(
        const __global double* A, const __global double* B, __global double* R,
        const int M, const int N, unsigned int lower_upper_A,
        unsigned int lower_upper_B) {
      const int gid = get_global_id(0);

      const int start = lower_upper_A == UPPER ? gid : 0;
      const int stop
          = lower_upper_B == UPPER ? 1 : (lower_upper_A == LOWER ? gid + 1 : N);

      double acc = 0;
      for (int i = start, j = M * start; i < stop; i++, j += M) {
        acc += A[j + gid] * B[i];
      }
      R[gid] = acc;
    }
    // \cond
);
// \endcond

/**
 * See the docs for \link kernels/matrix_multiply.hpp matrix_vector_multiply()
 * \endlink
 */
const kernel_cl<in_buffer, in_buffer, out_buffer, int, int, TriangularViewCL,
                TriangularViewCL>
    matrix_vector_multiply("matrix_vector_multiply",
                           matrix_vector_multiply_kernel_code);

// \cond
static const char* row_vector_matrix_multiply_kernel_code = STRINGIFY(
    // \endcond
    /**
     * Row vector-matrix multiplication R=A*B on the OpenCL device
     *
     * @param[in] A row vector in row vector-matrix multiplication
     * @param[in] B matrix in row vector-matrix multiplication
     * @param[out] R the output vector
     * @param[in] N Number of cols for row vector A and number of rows for
     * matrix B
     * @param[in] K Number of cols for matrix B
     * @param[in] lower_upper_A the triangularity of A (lower, upper or none)
     * @param[in] lower_upper_B the triangularity of B (lower, upper or none)
     */
    __kernel void row_vector_matrix_multiply(
        const __global double* A, const __global double* B, __global double* R,
        const int N, const int K, unsigned int lower_upper_A,
        unsigned int lower_upper_B) {
      const int lid = get_local_id(0);
      const int gid = get_global_id(0);
      const int wgid = get_group_id(0);

      const int start = lower_upper_B == LOWER ? wgid : 0;
      const int stop = lower_upper_A == LOWER
                           ? 1
                           : (lower_upper_B == UPPER) ? wgid + 1 : N;

      double acc = 0;
      for (int i = lid + start; i < stop; i += LOCAL_SIZE_) {
        acc += A[i] * B[i + wgid * N];
      }

      __local double res_loc[LOCAL_SIZE_];
      res_loc[lid] = acc;
      barrier(CLK_LOCAL_MEM_FENCE);
      for (int step = LOCAL_SIZE_ / REDUCTION_STEP_SIZE; step > 0;
           step /= REDUCTION_STEP_SIZE) {
        if (lid < step) {
          for (int i = 1; i < REDUCTION_STEP_SIZE; i++) {
            res_loc[lid] += res_loc[lid + step * i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
      }
      if (lid == 0) {
        R[wgid] = res_loc[0];
      }
    }
    // \cond
);
// \endcond

/**
 * See the docs for \link kernels/matrix_multiply.hpp
 * row_vector_matrix_multiply() \endlink
 */
const kernel_cl<in_buffer, in_buffer, out_buffer, int, int, TriangularViewCL,
                TriangularViewCL>
    row_vector_matrix_multiply("row_vector_matrix_multiply",
                               row_vector_matrix_multiply_kernel_code,
                               {{"LOCAL_SIZE_", 64},
                                {"REDUCTION_STEP_SIZE", 4}});

}  // namespace opencl_kernels
}  // namespace math
}  // namespace stan
#endif
#endif
<commit_msg>optimizing triangular matrix multiplication<commit_after>#ifndef STAN_MATH_OPENCL_KERNELS_MATRIX_MULTIPLY_HPP
#define STAN_MATH_OPENCL_KERNELS_MATRIX_MULTIPLY_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_cl.hpp>
#include <stan/math/opencl/buffer_types.hpp>

namespace stan {
namespace math {
namespace opencl_kernels {
// \cond
static const char* matrix_multiply_kernel_code = STRINGIFY(
    // \endcond
    /**
     * Matrix multiplication on the OpenCL device
     *
     * @param[in] A the left matrix in matrix multiplication
     * @param[in] B the right matrix in matrix multiplication
     * @param[out] C the output matrix
     * @param[in] M Number of rows for matrix A
     * @param[in] N Number of cols for matrix B
     * @param[in] K Number of cols for matrix A and number of rows for matrix B
     * @param[in] lower_upper_A the triangularity of A (lower, upper or none)
     * @param[in] lower_upper_B the triangularity of B (lower, upper or none)
     */
    __kernel void matrix_multiply(
        const __global double* A, const __global double* B, __global double* C,
        const int M, const int N, const int K, unsigned int lower_upper_A,
        unsigned int lower_upper_B) {
      // thread index inside the thread_block
      const int thread_block_row = get_local_id(0);
      const int thread_block_col = get_local_id(1);
      // global thread index
      const int i = THREAD_BLOCK_SIZE * get_group_id(0) + thread_block_row;
      const int j = THREAD_BLOCK_SIZE * get_group_id(1) + thread_block_col;
      // identify if the matrix multiply is split
      const int split_id = get_global_id(2);
      const int split_size = get_global_size(2);
      // local memory
      __local double A_local[THREAD_BLOCK_SIZE][THREAD_BLOCK_SIZE];
      __local double B_local[THREAD_BLOCK_SIZE][THREAD_BLOCK_SIZE];

      double acc[WORK_PER_THREAD];
      for (int w = 0; w < WORK_PER_THREAD; w++) {
        acc[w] = 0.0;
      }
      // the number of tiles for each scalar product in the matrix mulitply
      const int num_tiles = (K + THREAD_BLOCK_SIZE - 1) / THREAD_BLOCK_SIZE;
      // in case of splitting the matrix multiply we need
      // use split_offset_tiles the threads assigned part
      // of the scalar products, while the split_tiles
      // determines the number of tiles a thread multiplies
      // if split_size = 1, each thread calculates the
      // the entire scalar product for all assigned
      // elements of the resulting matrix, meaning that
      // split_offset_tiles is 0 and split_tiles = num_tiles
      int split_tiles = num_tiles / split_size;
      const int split_remainder = num_tiles % split_size;
      int split_offset_tiles = split_id * split_tiles;
      if (split_id < split_remainder) {
        split_offset_tiles = split_offset_tiles + split_id;
        split_tiles++;
      } else {
        split_offset_tiles = split_offset_tiles + split_remainder;
      }
      // This kernel is based on the well known
      // general matrix multiplication kernels that
      // use tiling for shared memory
      // In cases where a matrix is lower triangular
      // its not necessary to multiply the elements
      // over the diagonal, therefore those tiles
      // in the matrix multiply can be skipped.
      // With upper triangular matrices we dont need
      // to multiply the elements under the diagonal,
      // so those tiles can be skipped.
      // The following code determines the start and
      // end tile based on triangularity of the input matrices
      // If no matrices are triangular the starting tile
      // is 0 and the end tile is num_tiles-1 which
      // is then a general matrix multiply
      const int end_tile_A
          = lower_upper_A == LOWER ? (i / THREAD_BLOCK_SIZE) : (num_tiles - 1);
      const int end_tile_B
          = lower_upper_B == UPPER ? (j / THREAD_BLOCK_SIZE) : (num_tiles - 1);
      const int start_tile_A
          = lower_upper_A == UPPER ? (i / THREAD_BLOCK_SIZE) : 0;
      const int start_tile_B
          = lower_upper_B == LOWER ? (j / THREAD_BLOCK_SIZE) : 0;
      // the starting and end tiles for a thread are determined by
      // split_offset_tiles and split_tiles. If the input matrix is
      // triangular some tiles can be skipped in which case we
      // either start the scalar product at larger cols/rows
      // or end them at smaller cols/rows.
      int start_tile = max(start_tile_A, start_tile_B);
      start_tile = max(start_tile, split_offset_tiles);
      int end_tile = min(end_tile_A, end_tile_B);                      // NOLINT
      end_tile = min(end_tile, split_offset_tiles + split_tiles - 1);  // NOLINT
      //special handling of first block
      if(lower_upper_A == UPPER || lower_upper_B == LOWER){
        const int tiled_i = THREAD_BLOCK_SIZE * start_tile + thread_block_row;
        const int tiled_j = THREAD_BLOCK_SIZE * start_tile + thread_block_col;
        for (int w = 0; w < WORK_PER_THREAD; w++) {
          // For the tiles on the diagonal we can ignore the values over
          // the diagonal if the matrix is lower triangular or under
          // the diagonal if the matrix is upper triangular
          const A_curr_j = tiled_j + w * THREAD_BLOCK_SIZE_COL;
          const B_curr_j = j + w * THREAD_BLOCK_SIZE_COL;
          // check if the indexes are outside the matrix
          // or under/above the diagonal with upper/lower
          // triangular matrices
          if (A_curr_j >= K || i >= M
              || (lower_upper_A == LOWER && A_curr_j > i)
              || (lower_upper_A == UPPER && A_curr_j < i)) {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = 0.0;
          } else {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = A[A_curr_j * M + i];
          }
          if (B_curr_j >= N || tiled_i >= K
              || (lower_upper_B == LOWER && B_curr_j > tiled_i)
              || (lower_upper_B == UPPER && B_curr_j < tiled_i)) {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = 0.0;
          } else {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = B[B_curr_j * K + tiled_i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        for (int block_idx = 0; block_idx < THREAD_BLOCK_SIZE; block_idx++) {
          for (int w = 0; w < WORK_PER_THREAD; w++) {
            acc[w] += A_local[block_idx][thread_block_row]
                      * B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                      [block_idx];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        start_tile++;
      }
      //special handling of last block
      if(start_tile <= end_tile && (lower_upper_A == LOWER || lower_upper_B == UPPER)){
        const int tiled_i = THREAD_BLOCK_SIZE * end_tile + thread_block_row;
        const int tiled_j = THREAD_BLOCK_SIZE * end_tile + thread_block_col;
        for (int w = 0; w < WORK_PER_THREAD; w++) {
          // For the tiles on the diagonal we can ignore the values over
          // the diagonal if the matrix is lower triangular or under
          // the diagonal if the matrix is upper triangular
          const A_curr_j = tiled_j + w * THREAD_BLOCK_SIZE_COL;
          const B_curr_j = j + w * THREAD_BLOCK_SIZE_COL;
          // check if the indexes are outside the matrix
          // or under/above the diagonal with upper/lower
          // triangular matrices
          if (A_curr_j >= K || i >= M
              || (lower_upper_A == LOWER && A_curr_j > i)
              || (lower_upper_A == UPPER && A_curr_j < i)) {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = 0.0;
          } else {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = A[A_curr_j * M + i];
          }
          if (B_curr_j >= N || tiled_i >= K
              || (lower_upper_B == LOWER && B_curr_j > tiled_i)
              || (lower_upper_B == UPPER && B_curr_j < tiled_i)) {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = 0.0;
          } else {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
            [thread_block_row]
                    = B[B_curr_j * K + tiled_i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        for (int block_idx = 0; block_idx < THREAD_BLOCK_SIZE; block_idx++) {
          for (int w = 0; w < WORK_PER_THREAD; w++) {
            acc[w] += A_local[block_idx][thread_block_row]
                      * B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                      [block_idx];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        end_tile--;
      }
      for (int tile_idx = start_tile; tile_idx <= end_tile; tile_idx++) {
        const int tiled_i = THREAD_BLOCK_SIZE * tile_idx + thread_block_row;
        const int tiled_j = THREAD_BLOCK_SIZE * tile_idx + thread_block_col;
        // each thread copies WORK_PER_THREAD values to the local
        // memory
        for (int w = 0; w < WORK_PER_THREAD; w++) {
          // For the tiles on the diagonal we can ignore the values over
          // the diagonal if the matrix is lower triangular or under
          // the diagonal if the matrix is upper triangular
          const A_curr_j = tiled_j + w * THREAD_BLOCK_SIZE_COL;
          const B_curr_j = j + w * THREAD_BLOCK_SIZE_COL;
          // check if the indexes are outside the matrix
          // or under/above the diagonal with upper/lower
          // triangular matrices
          if (A_curr_j >= K || i >= M) {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = 0.0;
          } else {
            A_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = A[A_curr_j * M + i];
          }
          if (B_curr_j >= N || tiled_i >= K) {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = 0.0;
          } else {
            B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                   [thread_block_row]
                = B[B_curr_j * K + tiled_i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        for (int block_idx = 0; block_idx < THREAD_BLOCK_SIZE; block_idx++) {
          for (int w = 0; w < WORK_PER_THREAD; w++) {
            acc[w] += A_local[block_idx][thread_block_row]
                      * B_local[thread_block_col + w * THREAD_BLOCK_SIZE_COL]
                               [block_idx];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
      }
      // each thread saves WORK_PER_THREAD values
      for (int w = 0; w < WORK_PER_THREAD; w++) {
        // This prevents threads from accessing elements
        // outside the allocated memory for C. The check
        // is in the loop because some threads
        // can be assigned elements in and out of
        // the allocated memory.
        if ((j + w * THREAD_BLOCK_SIZE_COL) < N && i < M) {
          C[split_id * M * N + (j + w * THREAD_BLOCK_SIZE_COL) * M + i]
              = acc[w];
        }
      }
    }
    // \cond
);
// \endcond

/**
 * See the docs for \link kernels/matrix_multiply.hpp matrix_multiply() \endlink
 */
const kernel_cl<in_buffer, in_buffer, out_buffer, int, int, int,
                TriangularViewCL, TriangularViewCL>
    matrix_multiply("matrix_multiply",
                    {thread_block_helpers, matrix_multiply_kernel_code},
                    {{"THREAD_BLOCK_SIZE", 32}, {"WORK_PER_THREAD", 8}});

// \cond
static const char* matrix_vector_multiply_kernel_code = STRINGIFY(
    // \endcond
    /**
     * Matrix-vector multiplication R=A*B on the OpenCL device
     *
     * @param[in] A matrix in matrix-vector multiplication
     * @param[in] B vector in matrix-vector multiplication
     * @param[out] R the output vector
     * @param[in] M Number of rows for matrix A
     * @param[in] N Number of cols for matrix A and number of rows for vector B
     * @param[in] lower_upper_A the triangularity of A (lower, upper or none)
     * @param[in] lower_upper_B the triangularity of B (lower, upper or none)
     */
    __kernel void matrix_vector_multiply(
        const __global double* A, const __global double* B, __global double* R,
        const int M, const int N, unsigned int lower_upper_A,
        unsigned int lower_upper_B) {
      const int gid = get_global_id(0);

      const int start = lower_upper_A == UPPER ? gid : 0;
      const int stop
          = lower_upper_B == UPPER ? 1 : (lower_upper_A == LOWER ? gid + 1 : N);

      double acc = 0;
      for (int i = start, j = M * start; i < stop; i++, j += M) {
        acc += A[j + gid] * B[i];
      }
      R[gid] = acc;
    }
    // \cond
);
// \endcond

/**
 * See the docs for \link kernels/matrix_multiply.hpp matrix_vector_multiply()
 * \endlink
 */
const kernel_cl<in_buffer, in_buffer, out_buffer, int, int, TriangularViewCL,
                TriangularViewCL>
    matrix_vector_multiply("matrix_vector_multiply",
                           matrix_vector_multiply_kernel_code);

// \cond
static const char* row_vector_matrix_multiply_kernel_code = STRINGIFY(
    // \endcond
    /**
     * Row vector-matrix multiplication R=A*B on the OpenCL device
     *
     * @param[in] A row vector in row vector-matrix multiplication
     * @param[in] B matrix in row vector-matrix multiplication
     * @param[out] R the output vector
     * @param[in] N Number of cols for row vector A and number of rows for
     * matrix B
     * @param[in] K Number of cols for matrix B
     * @param[in] lower_upper_A the triangularity of A (lower, upper or none)
     * @param[in] lower_upper_B the triangularity of B (lower, upper or none)
     */
    __kernel void row_vector_matrix_multiply(
        const __global double* A, const __global double* B, __global double* R,
        const int N, const int K, unsigned int lower_upper_A,
        unsigned int lower_upper_B) {
      const int lid = get_local_id(0);
      const int gid = get_global_id(0);
      const int wgid = get_group_id(0);

      const int start = lower_upper_B == LOWER ? wgid : 0;
      const int stop = lower_upper_A == LOWER
                           ? 1
                           : (lower_upper_B == UPPER) ? wgid + 1 : N;

      double acc = 0;
      for (int i = lid + start; i < stop; i += LOCAL_SIZE_) {
        acc += A[i] * B[i + wgid * N];
      }

      __local double res_loc[LOCAL_SIZE_];
      res_loc[lid] = acc;
      barrier(CLK_LOCAL_MEM_FENCE);
      for (int step = LOCAL_SIZE_ / REDUCTION_STEP_SIZE; step > 0;
           step /= REDUCTION_STEP_SIZE) {
        if (lid < step) {
          for (int i = 1; i < REDUCTION_STEP_SIZE; i++) {
            res_loc[lid] += res_loc[lid + step * i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
      }
      if (lid == 0) {
        R[wgid] = res_loc[0];
      }
    }
    // \cond
);
// \endcond

/**
 * See the docs for \link kernels/matrix_multiply.hpp
 * row_vector_matrix_multiply() \endlink
 */
const kernel_cl<in_buffer, in_buffer, out_buffer, int, int, TriangularViewCL,
                TriangularViewCL>
    row_vector_matrix_multiply("row_vector_matrix_multiply",
                               row_vector_matrix_multiply_kernel_code,
                               {{"LOCAL_SIZE_", 64},
                                {"REDUCTION_STEP_SIZE", 4}});

}  // namespace opencl_kernels
}  // namespace math
}  // namespace stan
#endif
#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2013 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Teapotnet.                                     *
 *                                                                       *
 *   Teapotnet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Teapotnet 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 Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Teapotnet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/interface.h"
#include "tpn/html.h"
#include "tpn/user.h"
#include "tpn/store.h"
#include "tpn/splicer.h"
#include "tpn/config.h"
#include "tpn/directory.h"
#include "tpn/mime.h"

namespace tpn
{

Interface *Interface::Instance = NULL;

Interface::Interface(int port) :
		Http::Server(port)
{

}

Interface::~Interface(void)
{

}

void Interface::add(const String &prefix, HttpInterfaceable *interfaceable)
{
	Assert(interfaceable != NULL);

	String cprefix(prefix);
	if(cprefix.empty() || cprefix[0] != '/')
		cprefix = "/" + cprefix;

	mMutex.lock();
	mPrefixes.insert(cprefix, interfaceable);
	mMutex.unlock();
}

void Interface::remove(const String &prefix, HttpInterfaceable *interfaceable)
{
	mMutex.lock();
	HttpInterfaceable *test = NULL;
	if(mPrefixes.get(prefix, test) && (!interfaceable || test == interfaceable))
		mPrefixes.erase(prefix);
	mMutex.unlock();
}

void Interface::process(Http::Request &request)
{
	Address remoteAddr = request.sock->getRemoteAddress();

	LogDebug("Interface", "Request for URL \""+request.fullUrl+"\"");

	// URL must begin with /
	if(request.url.empty() || request.url[0] != '/') throw 404;

	if(request.url == "/")
	{
		if(request.method == "POST")
		{
			String name, password, tracker;
			request.post.get("name", name);
			request.post.get("password", password);
			request.post.get("tracker", tracker);

			if(name.contains('@'))
				tracker = name.cut('@');

			User *user = NULL;
			try {
				if(request.post.contains("create") && !User::Exist(name))
				{
					user = new User(name, password, tracker);
				}
				else {
					user = User::Authenticate(name, password);
					if(user && !tracker.empty())
						user->setTracker(tracker);
				}
			}
			catch(const Exception &e)
			{
				Http::Response response(request, 200);
				response.send();

				Html page(response.sock);
				page.header("Error", false, "/");
				page.text(e.what());
				page.footer();
				return;
			}

			if(!user) throw 401;	// TODO

			String token = user->generateToken("auth");

			Http::Response response(request, 303);
			response.headers["Location"] = "/" + user->name();
			response.cookies["auth_"+user->name()] = token;
			response.send();
			return;
		}

/*
#ifdef ANDROID
		if(!user && remoteAddr.isLocal() && User::Count() == 1)
		{
			Array<String> names;
			User::GetNames(names);
			user = User::Get(names[0]);

			if(user)
			{
				Http::Response response(request, 303);
				response.headers["Location"] = "/" + user->name();
				response.send();
				return;
			}
		}
#endif
*/
		Http::Response response(request, 200);
		response.send();

		Html page(response.sock);
		page.header("Teapotnet - Login", true);
		page.open("div","login");
		page.open("div","logo");
		page.openLink("/");
		page.image("/logo.png", "Teapotnet");
		page.closeLink();
		page.close("div");

		page.openForm("/", "post");
		page.open("table");
		page.open("tr");
		page.open("td",".label"); page.label("name", "Name"); page.close("td");
		page.open("td"); page.input("text", "name"); page.close("td");
		page.open("td"); page.link("#", "Change trackers", "trackerlink"); page.close("td");
		page.close("tr");
		page.open("tr", "trackerselection");
		page.open("td",".label"); page.label("tracker", "Tracker"); page.close("td");
		page.open("td"); page.input("tracker", "tracker", ""); page.close("td");
		page.open("td"); page.close("td");
		page.close("tr");
		page.open("tr");
		page.open("td",".label"); page.label("password", "Password"); page.close("td");
		page.open("td"); page.input("password", "password"); page.close("td");
		page.open("td"); page.close("td");
		page.close("tr");
		page.open("tr");
		page.open("td",".label"); page.close("td");
		page.open("td"); if(User::Count() > 0) page.button("login", "Login"); page.button("create", "Create"); page.close("td");
		page.close("tr");
		page.close("table");
		page.closeForm();

		for(StringMap::iterator it = request.cookies.begin();
			it != request.cookies.end();
			++it)
		{
			String cookieName = it->first;
			String name = cookieName.cut('_');
			if(cookieName != "auth" || name.empty())
				continue;

			User *user = User::Get(name);
			if(!user || !user->checkToken(it->second, "auth"))
				continue;

			page.open("div",".user");
			page.openLink("/" + name);
			page.image(user->profile()->avatarUrl(), "", ".avatar");
			page.open("span", ".username");
			page.text(name);
			page.close("span");
			page.closeLink();
			page.text(" - ");
			page.link("#", "Logout", ".logoutlink");
			page.close("div");

			page.javascript("$('.user a.logoutlink').click(function() {\n\
				unsetCookie('auth_'+$(this).parent().find('.username').text());\n\
				window.location.reload();\n\
				return false;\n\
			});");
		}

		page.close("div");

		page.javascript("$('#trackerselection').hide();\n\
			$('#trackerlink').click(function() {\n\
				$(this).hide();\n\
				$('#trackerselection').show();\n\
				$('#trackerselection .tracker').val('"+Config::Get("tracker")+"');\n\
			});");

		page.footer();
		return;
	}

	List<String> list;
	request.url.explode(list,'/');
	list.pop_front();	// first element is empty because url begin with '/'
	if(list.empty()) throw 500;
	if(list.front().empty()) throw 404;

	if(list.size() == 1 && list.front().contains('.') && request.url[request.url.size()-1] != '/')
	{
		String fileName = Config::Get("static_dir") + Directory::Separator + list.front();
		if(File::Exist(fileName))
		{
			Http::RespondWithFile(request, fileName);
			return;
		}

		if(!User::Exist(list.front())) throw 404;
	}

	if(User::Exist(list.front()))
	{
		String name = list.front();

		String token;
		request.cookies.get("auth_"+name, token);

		User *user = User::Get(list.front());
		if(!user || !user->checkToken(token, "auth"))
		{
			Http::Response response(request, 303);
			response.headers["Location"] = "/";
			response.send();
			return;
		}

		while(!list.empty())
		{
			String prefix;
			prefix.implode(list,'/');
			prefix = "/" + prefix;
			list.pop_back();

			mMutex.lock();
			HttpInterfaceable *interfaceable;
			if(mPrefixes.get(prefix,interfaceable))
			{
				mMutex.unlock();

				request.url.ignore(prefix.size());

				LogDebug("Interface", "Matched prefix \""+prefix+"\"");

				if(prefix != "/" && request.url.empty())
				{
					Http::Response response(request, 301);	// Moved Permanently
					response.headers["Location"] = prefix+"/";
					response.send();
					return;
				}

				interfaceable->http(prefix, request);
				return;
			}
			mMutex.unlock();
		}
	}
	else {
	  	if(list.size() != 1) throw 404;

	 	try {
			ByteString digest;
			String tmp = list.front();
			try { tmp >> digest; }
			catch(...) { throw 404; }

			if(request.get.contains("play") || request.get.contains("playlist"))
			{
				String host;
				if(!request.headers.get("Host", host))
				host = String("localhost:") + Config::Get("interface_port");

				Http::Response response(request, 200);
				response.headers["Content-Disposition"] = "attachment; filename=\"stream.m3u\"";
				response.headers["Content-Type"] = "audio/x-mpegurl";
				response.send();

				response.sock->writeLine("#EXTM3U");
				response.sock->writeLine(String("#EXTINF:-1, ") + APPNAME + " stream");
				response.sock->writeLine("http://" + host + "/" + digest.toString());
				response.sock->close();

				try {
					// Request the sources now to gain some time afterwards
					Resource resource(digest);
					resource.fetch();
				}
				catch(...) {}
				return;
			}

			// Query resource
			Resource resource(digest);
			resource.fetch();		// this can take some time

			// Get range
			int64_t rangeBegin = 0;
			int64_t rangeEnd = 0;
			bool hasRange = request.extractRange(rangeBegin, rangeEnd, resource.size());
			int64_t rangeSize = rangeEnd - rangeBegin + 1;

			// Get resource accessor
			Resource::Accessor *accessor = resource.accessor();
			if(!accessor) throw 404;

			// Forge HTTP response header
			Http::Response response(request, 200);
			if(!hasRange) response.headers["Content-SHA512"] << resource.digest();
			response.headers["Content-Length"] << rangeSize;
			response.headers["Content-Name"] = resource.name();
			response.headers["Last-Modified"] = resource.time().toHttpDate();
			response.headers["Accept-Ranges"] = "bytes";

			String ext = resource.name().afterLast('.');
			if(request.get.contains("download") || ext == "htm" || ext == "html" || ext == "xhtml")
			{
				response.headers["Content-Disposition"] = "attachment; filename=\"" + resource.name() + "\"";
				response.headers["Content-Type"] = "application/force-download";
			}
			else {
				response.headers["Content-Disposition"] = "inline; filename=\"" + resource.name() + "\"";
				response.headers["Content-Type"] = Mime::GetType(resource.name());
			}

			response.send();
			if(request.method == "HEAD") return;

			try {
				// Launch transfer
				if(hasRange) accessor->seekRead(rangeBegin);
				int64_t size = accessor->readBinary(*response.sock, rangeSize);	// let's go !
				if(size != rangeSize)
					throw Exception("range size is " + String::number(rangeSize) + ", but sent size is " + String::number(size));
			}
			catch(const NetException &e)
			{
				return;	// nothing to do
			}
			catch(const Exception &e)
			{
				LogWarn("Interface::process", String("Error during file transfer: ") + e.what());
			}

			return;
		}
		catch(const NetException &e)
		{
			 return;	// nothing to do
		}
		catch(const std::exception &e)
		{
			LogWarn("Interface::process", e.what());
			throw 404;
		}
	}

	throw 404;
}

}
<commit_msg>Fixed page title<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2013 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Teapotnet.                                     *
 *                                                                       *
 *   Teapotnet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Teapotnet 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 Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Teapotnet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/interface.h"
#include "tpn/html.h"
#include "tpn/user.h"
#include "tpn/store.h"
#include "tpn/splicer.h"
#include "tpn/config.h"
#include "tpn/directory.h"
#include "tpn/mime.h"

namespace tpn
{

Interface *Interface::Instance = NULL;

Interface::Interface(int port) :
		Http::Server(port)
{

}

Interface::~Interface(void)
{

}

void Interface::add(const String &prefix, HttpInterfaceable *interfaceable)
{
	Assert(interfaceable != NULL);

	String cprefix(prefix);
	if(cprefix.empty() || cprefix[0] != '/')
		cprefix = "/" + cprefix;

	mMutex.lock();
	mPrefixes.insert(cprefix, interfaceable);
	mMutex.unlock();
}

void Interface::remove(const String &prefix, HttpInterfaceable *interfaceable)
{
	mMutex.lock();
	HttpInterfaceable *test = NULL;
	if(mPrefixes.get(prefix, test) && (!interfaceable || test == interfaceable))
		mPrefixes.erase(prefix);
	mMutex.unlock();
}

void Interface::process(Http::Request &request)
{
	Address remoteAddr = request.sock->getRemoteAddress();

	LogDebug("Interface", "Request for URL \""+request.fullUrl+"\"");

	// URL must begin with /
	if(request.url.empty() || request.url[0] != '/') throw 404;

	if(request.url == "/")
	{
		if(request.method == "POST")
		{
			String name, password, tracker;
			request.post.get("name", name);
			request.post.get("password", password);
			request.post.get("tracker", tracker);

			if(name.contains('@'))
				tracker = name.cut('@');

			User *user = NULL;
			try {
				if(request.post.contains("create") && !User::Exist(name))
				{
					user = new User(name, password, tracker);
				}
				else {
					user = User::Authenticate(name, password);
					if(user && !tracker.empty())
						user->setTracker(tracker);
				}
			}
			catch(const Exception &e)
			{
				Http::Response response(request, 200);
				response.send();

				Html page(response.sock);
				page.header("Error", false, "/");
				page.text(e.what());
				page.footer();
				return;
			}

			if(!user) throw 401;	// TODO

			String token = user->generateToken("auth");

			Http::Response response(request, 303);
			response.headers["Location"] = "/" + user->name();
			response.cookies["auth_"+user->name()] = token;
			response.send();
			return;
		}

/*
#ifdef ANDROID
		if(!user && remoteAddr.isLocal() && User::Count() == 1)
		{
			Array<String> names;
			User::GetNames(names);
			user = User::Get(names[0]);

			if(user)
			{
				Http::Response response(request, 303);
				response.headers["Location"] = "/" + user->name();
				response.send();
				return;
			}
		}
#endif
*/
		Http::Response response(request, 200);
		response.send();

		Html page(response.sock);
		page.header("Login - Teapotnet", true);
		page.open("div","login");
		page.open("div","logo");
		page.openLink("/");
		page.image("/logo.png", "Teapotnet");
		page.closeLink();
		page.close("div");

		page.openForm("/", "post");
		page.open("table");
		page.open("tr");
		page.open("td",".label"); page.label("name", "Name"); page.close("td");
		page.open("td"); page.input("text", "name"); page.close("td");
		page.open("td"); page.link("#", "Change trackers", "trackerlink"); page.close("td");
		page.close("tr");
		page.open("tr", "trackerselection");
		page.open("td",".label"); page.label("tracker", "Tracker"); page.close("td");
		page.open("td"); page.input("tracker", "tracker", ""); page.close("td");
		page.open("td"); page.close("td");
		page.close("tr");
		page.open("tr");
		page.open("td",".label"); page.label("password", "Password"); page.close("td");
		page.open("td"); page.input("password", "password"); page.close("td");
		page.open("td"); page.close("td");
		page.close("tr");
		page.open("tr");
		page.open("td",".label"); page.close("td");
		page.open("td"); if(User::Count() > 0) page.button("login", "Login"); page.button("create", "Create"); page.close("td");
		page.close("tr");
		page.close("table");
		page.closeForm();

		for(StringMap::iterator it = request.cookies.begin();
			it != request.cookies.end();
			++it)
		{
			String cookieName = it->first;
			String name = cookieName.cut('_');
			if(cookieName != "auth" || name.empty())
				continue;

			User *user = User::Get(name);
			if(!user || !user->checkToken(it->second, "auth"))
				continue;

			page.open("div",".user");
			page.openLink("/" + name);
			page.image(user->profile()->avatarUrl(), "", ".avatar");
			page.open("span", ".username");
			page.text(name);
			page.close("span");
			page.closeLink();
			page.text(" - ");
			page.link("#", "Logout", ".logoutlink");
			page.close("div");

			page.javascript("$('.user a.logoutlink').click(function() {\n\
				unsetCookie('auth_'+$(this).parent().find('.username').text());\n\
				window.location.reload();\n\
				return false;\n\
			});");
		}

		page.close("div");

		page.javascript("$('#trackerselection').hide();\n\
			$('#trackerlink').click(function() {\n\
				$(this).hide();\n\
				$('#trackerselection').show();\n\
				$('#trackerselection .tracker').val('"+Config::Get("tracker")+"');\n\
			});");

		page.footer();
		return;
	}

	List<String> list;
	request.url.explode(list,'/');
	list.pop_front();	// first element is empty because url begin with '/'
	if(list.empty()) throw 500;
	if(list.front().empty()) throw 404;

	if(list.size() == 1 && list.front().contains('.') && request.url[request.url.size()-1] != '/')
	{
		String fileName = Config::Get("static_dir") + Directory::Separator + list.front();
		if(File::Exist(fileName))
		{
			Http::RespondWithFile(request, fileName);
			return;
		}

		if(!User::Exist(list.front())) throw 404;
	}

	if(User::Exist(list.front()))
	{
		String name = list.front();

		String token;
		request.cookies.get("auth_"+name, token);

		User *user = User::Get(list.front());
		if(!user || !user->checkToken(token, "auth"))
		{
			Http::Response response(request, 303);
			response.headers["Location"] = "/";
			response.send();
			return;
		}

		while(!list.empty())
		{
			String prefix;
			prefix.implode(list,'/');
			prefix = "/" + prefix;
			list.pop_back();

			mMutex.lock();
			HttpInterfaceable *interfaceable;
			if(mPrefixes.get(prefix,interfaceable))
			{
				mMutex.unlock();

				request.url.ignore(prefix.size());

				LogDebug("Interface", "Matched prefix \""+prefix+"\"");

				if(prefix != "/" && request.url.empty())
				{
					Http::Response response(request, 301);	// Moved Permanently
					response.headers["Location"] = prefix+"/";
					response.send();
					return;
				}

				interfaceable->http(prefix, request);
				return;
			}
			mMutex.unlock();
		}
	}
	else {
	  	if(list.size() != 1) throw 404;

	 	try {
			ByteString digest;
			String tmp = list.front();
			try { tmp >> digest; }
			catch(...) { throw 404; }

			if(request.get.contains("play") || request.get.contains("playlist"))
			{
				String host;
				if(!request.headers.get("Host", host))
				host = String("localhost:") + Config::Get("interface_port");

				Http::Response response(request, 200);
				response.headers["Content-Disposition"] = "attachment; filename=\"stream.m3u\"";
				response.headers["Content-Type"] = "audio/x-mpegurl";
				response.send();

				response.sock->writeLine("#EXTM3U");
				response.sock->writeLine(String("#EXTINF:-1, ") + APPNAME + " stream");
				response.sock->writeLine("http://" + host + "/" + digest.toString());
				response.sock->close();

				try {
					// Request the sources now to gain some time afterwards
					Resource resource(digest);
					resource.fetch();
				}
				catch(...) {}
				return;
			}

			// Query resource
			Resource resource(digest);
			resource.fetch();		// this can take some time

			// Get range
			int64_t rangeBegin = 0;
			int64_t rangeEnd = 0;
			bool hasRange = request.extractRange(rangeBegin, rangeEnd, resource.size());
			int64_t rangeSize = rangeEnd - rangeBegin + 1;

			// Get resource accessor
			Resource::Accessor *accessor = resource.accessor();
			if(!accessor) throw 404;

			// Forge HTTP response header
			Http::Response response(request, 200);
			if(!hasRange) response.headers["Content-SHA512"] << resource.digest();
			response.headers["Content-Length"] << rangeSize;
			response.headers["Content-Name"] = resource.name();
			response.headers["Last-Modified"] = resource.time().toHttpDate();
			response.headers["Accept-Ranges"] = "bytes";

			String ext = resource.name().afterLast('.');
			if(request.get.contains("download") || ext == "htm" || ext == "html" || ext == "xhtml")
			{
				response.headers["Content-Disposition"] = "attachment; filename=\"" + resource.name() + "\"";
				response.headers["Content-Type"] = "application/force-download";
			}
			else {
				response.headers["Content-Disposition"] = "inline; filename=\"" + resource.name() + "\"";
				response.headers["Content-Type"] = Mime::GetType(resource.name());
			}

			response.send();
			if(request.method == "HEAD") return;

			try {
				// Launch transfer
				if(hasRange) accessor->seekRead(rangeBegin);
				int64_t size = accessor->readBinary(*response.sock, rangeSize);	// let's go !
				if(size != rangeSize)
					throw Exception("range size is " + String::number(rangeSize) + ", but sent size is " + String::number(size));
			}
			catch(const NetException &e)
			{
				return;	// nothing to do
			}
			catch(const Exception &e)
			{
				LogWarn("Interface::process", String("Error during file transfer: ") + e.what());
			}

			return;
		}
		catch(const NetException &e)
		{
			 return;	// nothing to do
		}
		catch(const std::exception &e)
		{
			LogWarn("Interface::process", e.what());
			throw 404;
		}
	}

	throw 404;
}

}
<|endoftext|>