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

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

You may use this file under the terms of the BSD license as follows:

"Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."

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

#include "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"

#define DEBUG if (0) qDebug() << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
}

MediaModel::~MediaModel()
{
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    if (type == m_mediaType)
        return;

    initialize();

    m_mediaType = type;
    emit mediaTypeChanged();

    // Add the fields of the table as role names
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(DotDotRole, "dotdot");
    hash.insert(IsLeafRole, "isLeaf");
    hash.insert(PreviewUrlRole, "previewUrl");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(FieldRolesBegin + i, record.fieldName(i).toUtf8());
    }

    setRoleNames(hash);
}

void MediaModel::addSearchPath(const QString &path, const QString &name)
{
    if (m_mediaType.isEmpty()) {
        qWarning() << m_mediaType << " is not set yet";
        return;
    }

    QMetaObject::invokeMethod(MediaScanner::instance(), "addSearchPath", Qt::QueuedConnection,
                              Q_ARG(QString, m_mediaType), Q_ARG(QString, path), Q_ARG(QString, name));
}

void MediaModel::removeSearchPath(int index)
{
    Q_UNUSED(index);
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    m_structure = str;
    initialize();
    m_layoutInfo.clear();
    foreach(const QString &part, m_structure.split("|"))
        m_layoutInfo.append(part.split(","));

    emit structureChanged();
}

void MediaModel::enter(int index)
{
    if (m_cursor.count() + 1 == m_layoutInfo.count() && index != 0 /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (index == 0 && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;
    m_cursor.append(m_data[index]);
    initialize();
    emit partChanged();
}

void MediaModel::back()
{
    m_cursor.removeLast();
    initialize();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    return m_data.value(index.row()).value(role);
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_layoutInfo.isEmpty())
        return false;
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    m_loading = true;

    QSqlQuery q = buildQuery();
    DEBUG << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::initialize()
{
    DEBUG << "";

    beginResetModel();

    m_data.clear();

    DbReader *newReader = new DbReader;
    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = newReader;

    if (!m_readerThread) {
        m_readerThread = new QThread(this);
        m_readerThread->start();
    }
    m_reader->moveToThread(m_readerThread);
    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));

    m_loading = m_loaded = false;

    endResetModel();
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (records.isEmpty())
        return;

    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
        QHash<int, QVariant> data;
        data.insert(Qt::DisplayRole, tr(".."));
        data.insert(DotDotRole, true);
        data.insert(IsLeafRole, false);
        m_data.append(data);
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    const bool isLeaf = m_cursor.count() + 1 == m_layoutInfo.count();

    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    const QSqlRecord tableRecord = driver->record(m_mediaType);

    for (int i = 0; i < records.count(); i++) {
        QHash<int, QVariant> data;
        for (int j = 0; j < records[i].count(); j++) {
            int idx = tableRecord.indexOf(records[i].fieldName(j));
            data.insert(FieldRolesBegin + idx, records[i].value(j));
        }

        // Provide 'display' role as , separated values
        QStringList cols = m_layoutInfo.value(m_cursor.count());
        QStringList displayString;
        for (int j = 0; j < cols.count(); j++) {
            displayString << records[i].value(cols[j]).toString();
        }
        data.insert(Qt::DisplayRole, displayString.join(", "));
        data.insert(DotDotRole, false);
        data.insert(IsLeafRole, isLeaf);

        data.insert(PreviewUrlRole, records[i].value("thumbnail").toByteArray());

        m_data.append(data);
    }

    m_loading = false;
    m_loaded = true;

    endInsertRows();
}

void MediaModel::handleDatabaseUpdated(const QList<QSqlRecord> &records)
{
    Q_UNUSED(records);
    // not implemented yet
}

QSqlQuery MediaModel::buildQuery() const
{
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();

    QStringList escapedCurParts;
    QStringList curParts = m_layoutInfo[m_cursor.count()];
    for (int i = 0; i < curParts.count(); i++)
        escapedCurParts.append(driver->escapeIdentifier(curParts[i], QSqlDriver::FieldName));
    QString escapedCurPart = escapedCurParts.join(",");

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    QStringList placeHolders;
    const bool lastPart = m_cursor.count() == m_layoutInfo.count()-1;
    QString queryString;
    // SQLite allows us to select columns that are not present in the GROUP BY. We use this feature
    // to select thumbnails for non-leaf nodes
    queryString.append("SELECT *");
    queryString.append(" FROM " + driver->escapeIdentifier(m_mediaType, QSqlDriver::TableName));

    if (!m_cursor.isEmpty()) {
        QStringList where;
        const QSqlRecord tableRecord = driver->record(m_mediaType);
        for (int i = 0; i < m_cursor.count(); i++) {
            QStringList subParts = m_layoutInfo[i];
            for (int j = 0; j < subParts.count(); j++) {
                where.append(subParts[j] + " = ?");
                const int role = FieldRolesBegin + tableRecord.indexOf(subParts[j]);
                placeHolders << m_cursor[i].value(role).toString();
            }
        }

        queryString.append(" WHERE " + where.join(" AND "));
    }

    if (!lastPart)
        queryString.append(" GROUP BY " + escapedCurPart);

    queryString.append(" ORDER BY " + escapedCurPart);

    query.prepare(queryString);

    foreach(const QString &placeHolder, placeHolders)
        query.addBindValue(placeHolder);

    return query;
}

<commit_msg>Return a string since that is what QML url properties usually want<commit_after>/****************************************************************************

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

You may use this file under the terms of the BSD license as follows:

"Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."

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

#include "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"

#define DEBUG if (0) qDebug() << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
}

MediaModel::~MediaModel()
{
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    if (type == m_mediaType)
        return;

    initialize();

    m_mediaType = type;
    emit mediaTypeChanged();

    // Add the fields of the table as role names
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(DotDotRole, "dotdot");
    hash.insert(IsLeafRole, "isLeaf");
    hash.insert(PreviewUrlRole, "previewUrl");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(FieldRolesBegin + i, record.fieldName(i).toUtf8());
    }

    setRoleNames(hash);
}

void MediaModel::addSearchPath(const QString &path, const QString &name)
{
    if (m_mediaType.isEmpty()) {
        qWarning() << m_mediaType << " is not set yet";
        return;
    }

    QMetaObject::invokeMethod(MediaScanner::instance(), "addSearchPath", Qt::QueuedConnection,
                              Q_ARG(QString, m_mediaType), Q_ARG(QString, path), Q_ARG(QString, name));
}

void MediaModel::removeSearchPath(int index)
{
    Q_UNUSED(index);
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    m_structure = str;
    initialize();
    m_layoutInfo.clear();
    foreach(const QString &part, m_structure.split("|"))
        m_layoutInfo.append(part.split(","));

    emit structureChanged();
}

void MediaModel::enter(int index)
{
    if (m_cursor.count() + 1 == m_layoutInfo.count() && index != 0 /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (index == 0 && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;
    m_cursor.append(m_data[index]);
    initialize();
    emit partChanged();
}

void MediaModel::back()
{
    m_cursor.removeLast();
    initialize();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    return m_data.value(index.row()).value(role);
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_layoutInfo.isEmpty())
        return false;
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    m_loading = true;

    QSqlQuery q = buildQuery();
    DEBUG << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::initialize()
{
    DEBUG << "";

    beginResetModel();

    m_data.clear();

    DbReader *newReader = new DbReader;
    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = newReader;

    if (!m_readerThread) {
        m_readerThread = new QThread(this);
        m_readerThread->start();
    }
    m_reader->moveToThread(m_readerThread);
    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));

    m_loading = m_loaded = false;

    endResetModel();
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (records.isEmpty())
        return;

    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
        QHash<int, QVariant> data;
        data.insert(Qt::DisplayRole, tr(".."));
        data.insert(DotDotRole, true);
        data.insert(IsLeafRole, false);
        m_data.append(data);
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    const bool isLeaf = m_cursor.count() + 1 == m_layoutInfo.count();

    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    const QSqlRecord tableRecord = driver->record(m_mediaType);

    for (int i = 0; i < records.count(); i++) {
        QHash<int, QVariant> data;
        for (int j = 0; j < records[i].count(); j++) {
            int idx = tableRecord.indexOf(records[i].fieldName(j));
            data.insert(FieldRolesBegin + idx, records[i].value(j));
        }

        // Provide 'display' role as , separated values
        QStringList cols = m_layoutInfo.value(m_cursor.count());
        QStringList displayString;
        for (int j = 0; j < cols.count(); j++) {
            displayString << records[i].value(cols[j]).toString();
        }
        data.insert(Qt::DisplayRole, displayString.join(", "));
        data.insert(DotDotRole, false);
        data.insert(IsLeafRole, isLeaf);

        data.insert(PreviewUrlRole, records[i].value("thumbnail").toString());

        m_data.append(data);
    }

    m_loading = false;
    m_loaded = true;

    endInsertRows();
}

void MediaModel::handleDatabaseUpdated(const QList<QSqlRecord> &records)
{
    Q_UNUSED(records);
    // not implemented yet
}

QSqlQuery MediaModel::buildQuery() const
{
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();

    QStringList escapedCurParts;
    QStringList curParts = m_layoutInfo[m_cursor.count()];
    for (int i = 0; i < curParts.count(); i++)
        escapedCurParts.append(driver->escapeIdentifier(curParts[i], QSqlDriver::FieldName));
    QString escapedCurPart = escapedCurParts.join(",");

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    QStringList placeHolders;
    const bool lastPart = m_cursor.count() == m_layoutInfo.count()-1;
    QString queryString;
    // SQLite allows us to select columns that are not present in the GROUP BY. We use this feature
    // to select thumbnails for non-leaf nodes
    queryString.append("SELECT *");
    queryString.append(" FROM " + driver->escapeIdentifier(m_mediaType, QSqlDriver::TableName));

    if (!m_cursor.isEmpty()) {
        QStringList where;
        const QSqlRecord tableRecord = driver->record(m_mediaType);
        for (int i = 0; i < m_cursor.count(); i++) {
            QStringList subParts = m_layoutInfo[i];
            for (int j = 0; j < subParts.count(); j++) {
                where.append(subParts[j] + " = ?");
                const int role = FieldRolesBegin + tableRecord.indexOf(subParts[j]);
                placeHolders << m_cursor[i].value(role).toString();
            }
        }

        queryString.append(" WHERE " + where.join(" AND "));
    }

    if (!lastPart)
        queryString.append(" GROUP BY " + escapedCurPart);

    queryString.append(" ORDER BY " + escapedCurPart);

    query.prepare(queryString);

    foreach(const QString &placeHolder, placeHolders)
        query.addBindValue(placeHolder);

    return query;
}

<|endoftext|>


<commit_before>#include <imgui.h>
#include <imgui_internal.h>
#include "SeqUISequencer.h";
#include "SeqProjectHeaders.h";
#include "SeqSerializer.h";
#include "SeqString.h";
#include "SeqList.h";

SeqUISequencer::SeqUISequencer(SeqProject *project):
	project(project)
{
	Init();
}

SeqUISequencer::SeqUISequencer(SeqProject *project, SeqSerializer *serializer):
	project(project)
{
	Init();
	Deserialize(serializer);
}

void SeqUISequencer::Init()
{
	// alloc memory
	name = SeqString::Format("Sequencer##%d", project->NextWindowId());
	channelHeights = new SeqList<int>();
	for (int i = 0; i < project->GetChannelCount(); i++)
		channelHeights->Add(initialChannelHeight);
	// start listening for project changes
	project->AddActionHandler(this);
}

SeqUISequencer::~SeqUISequencer()
{
	// stop listening for project changes
	project->RemoveActionHandler(this);
	// free memory
	delete[] name;
	delete channelHeights;
}

void SeqUISequencer::ActionDone(const SeqAction action)
{
	switch (action.type)
	{
		case SeqActionType::AddChannel:
			channelHeights->Add(initialChannelHeight);
			break;
	}
}

void SeqUISequencer::ActionUndone(const SeqAction action)
{
	switch (action.type)
	{
		case SeqActionType::AddChannel:
			channelHeights->RemoveAt(channelHeights->Count() - 1);
			break;
	}
}

SeqWindowType SeqUISequencer::GetWindowType()
{
	return SeqWindowType::Sequencer;
}

void SeqUISequencer::Draw()
{
	bool isWindowNew = false;
	if (!ImGui::FindWindowByName(name))
		isWindowNew = true;
	bool isOpen = true;

	if (ImGui::Begin(name, &isOpen, ImGuiWindowFlags_::ImGuiWindowFlags_NoScrollbar))
	{
		const static ImVec2 textSize = ImGui::CalcTextSize("01234567889:.");
		const float headerHeight = textSize.y + 3;
		ImGui::Columns(2, "panel");
		DrawChannelSettings(headerHeight, isWindowNew);
		ImGui::NextColumn();
		ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() - 4, ImGui::GetCursorPosY() + 1));
		DrawSequencerRuler(headerHeight);
		DrawChannels();
		ImGui::Columns(1);
	}
	ImGui::End();
	if (!isOpen)
		project->RemoveWindow(this);
}

void SeqUISequencer::DrawChannelSettings(float rulerHeight, bool isWindowNew)
{
	const ImGuiStyle style = ImGui::GetStyle();
	const ImVec2 origin = ImGui::GetCursorScreenPos();
	const ImVec2 size = ImVec2(settingsPanelWidth, ImGui::GetContentRegionAvail().y);

	// draw channel settings
	ImDrawList* drawList = ImGui::GetWindowDrawList();
	const ImU32 color = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_TextDisabled]);
	const ImU32 bgColor = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_ComboBg]);

	// default panel width
	if (isWindowNew)
		ImGui::SetColumnOffset(1, settingsPanelWidth + 7);

	// debug text
	ImGui::Text("%s", name);

	ImVec2 cursor = origin;
	cursor.y += rulerHeight + 1;
	ImGui::PushClipRect(cursor, ImVec2(cursor.x + size.x, cursor.y + size.y - rulerHeight - style.ScrollbarSize), false);
	// draw settings panels
	for (int i = 0; i < project->GetChannelCount(); i++)
	{
		float channelHeight = channelHeights->Get(i);
		// only draw if visible
		if (cursor.y - origin.y + channelHeight >= scrollY &&
			cursor.y - origin.y < scrollY + size.y)
		{
			drawList->AddRectFilled(ImVec2(cursor.x, cursor.y - scrollY), ImVec2(cursor.x + size.x, cursor.y - scrollY + channelHeight), bgColor, rounding, 0x1 | 0x8);
			drawList->AddRect(ImVec2(cursor.x, cursor.y - scrollY), ImVec2(cursor.x + size.x, cursor.y - scrollY + channelHeight), color, rounding, 0x1 | 0x8, lineThickness);
			ImGui::SetCursorScreenPos(ImVec2(cursor.x + 4, cursor.y - scrollY + 4));
			ImGui::Text("%s %d", project->GetChannel(i).name, i);
		}
		cursor.y += channelHeight + channelVerticalSpacing;
	}
	// draw and handle row resizing
	// code based on ImGui::Column()
	cursor = origin;
	cursor.y += rulerHeight;
	ImGuiContext* g = ImGui::GetCurrentContext();
	for (int i = 0; i < project->GetChannelCount(); i++)
	{
		float channelHeight = channelHeights->Get(i);
		cursor.y += channelHeight;
		// if visible
		if (cursor.y - origin.y >= scrollY &&
			cursor.y - origin.y < scrollY + size.y)
		{
			const ImGuiID row_id = ImGui::GetID("row") + ImGuiID(i);
			ImGui::KeepAliveID(row_id);
			const ImRect row_rect(ImVec2(cursor.x, cursor.y - scrollY - 4), ImVec2(cursor.x + size.x, cursor.y - scrollY + 4));

			bool hovered, held;
			ImGui::ButtonBehavior(row_rect, row_id, &hovered, &held);
			if (hovered || held)
				g->MouseCursor = ImGuiMouseCursor_ResizeNS;

			// Draw before resize so our items positioning are in sync with the line being drawn
			const ImU32 col = ImGui::GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
			drawList->AddLine(ImVec2(cursor.x + rounding, cursor.y - scrollY), ImVec2(cursor.x + size.x, cursor.y - scrollY), col);

			if (held)
			{
				if (g->ActiveIdIsJustActivated)
					g->ActiveIdClickOffset.y -= 4;   // Store from center of row line (we used a 8 high rect for row clicking)

				ImGuiWindow* window = ImGui::GetCurrentWindowRead();
				float newHeight = ImClamp(g->IO.MousePos.y - (cursor.y - channelHeight - scrollY) - g->ActiveIdClickOffset.y, minChannelHeight, maxChannelHeight);
				channelHeights->Set(i, newHeight);
			}
		}
		cursor.y += channelVerticalSpacing;
	}
	ImGui::PopClipRect();
	// clamp width
	settingsPanelWidth = ImGui::GetColumnOffset(1) - 7;
	if (settingsPanelWidth < minSettingsPanelWidth)
	{
		settingsPanelWidth = minSettingsPanelWidth;
		ImGui::SetColumnOffset(1, settingsPanelWidth);
	}
	if (settingsPanelWidth > maxSettingsPanelWidth)
	{
		settingsPanelWidth = maxSettingsPanelWidth;
		ImGui::SetColumnOffset(1, settingsPanelWidth);
	}
}

void SeqUISequencer::DrawSequencerRuler(float height)
{
	const ImGuiStyle style = ImGui::GetStyle();

	const ImVec2 origin = ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
	const float width = ImGui::GetContentRegionAvailWidth() - style.ScrollbarSize;
	const float thickness = 1.0f;
	const ImU32 color = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_TextDisabled]);

	// clip so we won't draw on top of the settings panel
	ImGui::PushClipRect(origin, ImVec2(origin.x + width, origin.y + height), false);

	ImDrawList* drawList = ImGui::GetWindowDrawList();
	// underline
	drawList->AddLine(ImVec2(origin.x, origin.y + height - 2), ImVec2(origin.x + width, origin.y + height - 2), color, thickness);

	// seconds lines
	int seconds = ceil(position);
	float firstOffset = -(position - floor(position)) * pixelsPerSecond / zoom;
	float scaledWidth = (origin.x + width) / zoom;
	float scaledStep = pixelsPerSecond / zoom;
	for (float x = origin.x + firstOffset; x < origin.x + scaledWidth; x += scaledStep)
	{
		drawList->AddLine(ImVec2(x, origin.y), ImVec2(x, origin.y + height - 1), color, thickness);
		ImGui::SetCursorScreenPos(ImVec2(x + style.FramePadding.x, origin.y));
		ImGui::Text("%02d\:%02d\.%d", (seconds - (seconds % 60)), seconds % 60, 0);
		seconds++;
	}
	ImGui::PopClipRect();

	// setup the position for channel drawing
	ImGui::SetCursorScreenPos(ImVec2(origin.x, origin.y + height));
}

void SeqUISequencer::DrawChannels()
{
	const ImGuiStyle style = ImGui::GetStyle();
	const ImVec2 contentSize = ImVec2(project->GetLength() * pixelsPerSecond / zoom, TotalChannelHeight() + 100);
	const ImVec2 size = ImVec2(ImGui::GetContentRegionAvail().x - style.ScrollbarSize, ImGui::GetContentRegionAvail().y - style.ScrollbarSize);
	ImGui::SetNextWindowContentSize(contentSize);
	ImGui::BeginChild("channels", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
	{
		position = ImGui::GetScrollX() / pixelsPerSecond * zoom;
		scrollY = ImGui::GetScrollY();

		const ImVec2 origin = ImGui::GetCursorScreenPos();

		ImDrawList* drawList = ImGui::GetWindowDrawList();

		const ImU32 color = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_TextDisabled]);
		const ImU32 bgColor = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_ComboBg]);

		ImVec2 cursor = origin;
		for (int i = 0; i < project->GetChannelCount(); i++)
		{
			float channelHeight = channelHeights->Get(i);
			// only draw if visible
			if (cursor.y - origin.y + channelHeight >= scrollY &&
				cursor.y - origin.y < scrollY + size.y)
			{
				drawList->AddRectFilled(ImVec2(cursor.x + ImGui::GetScrollX(), cursor.y), ImVec2(cursor.x + contentSize.x, cursor.y + channelHeight), bgColor, rounding, 0x2 | 0x4);
				drawList->AddRect(ImVec2(cursor.x, cursor.y), ImVec2(cursor.x + contentSize.x, cursor.y + channelHeight), color, rounding, 0x2 | 0x4, lineThickness);
				//ImGui::SetCursorScreenPos(ImVec2(cursor.x + ImGui::GetScrollX() + 4, cursor.y + 4));
				//ImGui::Text("%s %d", project->GetChannel(i).name, i);
			}
			cursor.y += channelHeight + channelVerticalSpacing;
		}

		static ImVec2 scrollPosTextSize = ImGui::CalcTextSize("ScrollPos: 00000.000, 00000.000");
		ImGui::SetCursorPos(ImVec2(ImGui::GetScrollX() + size.x - scrollPosTextSize.x, ImGui::GetScrollY() + size.y - scrollPosTextSize.y));
		ImGui::Text("ScrollPos: %.3f, %.3f", ImGui::GetScrollX(), ImGui::GetScrollY());
	}
	ImGui::EndChild();
}

int SeqUISequencer::TotalChannelHeight()
{
	int totalHeight = 0;
	for (int i = 0; i < channelHeights->Count(); i++)
		totalHeight += channelHeights->Get(i) + channelVerticalSpacing;
	// remove spacing on the bottom most channel
	totalHeight -= channelVerticalSpacing;
	return totalHeight;
}

void SeqUISequencer::Serialize(SeqSerializer *serializer)
{
	serializer->Write(position);
	serializer->Write(zoom);
	serializer->Write(scrollY);
	serializer->Write(settingsPanelWidth);
	serializer->Write(channelHeights->Count());
	for (int i = 0; i < channelHeights->Count(); i++)
		serializer->Write(channelHeights->Get(i));
}

void SeqUISequencer::Deserialize(SeqSerializer *serializer)
{
	position = serializer->ReadDouble();
	zoom = serializer->ReadFloat();
	scrollY = serializer->ReadFloat();
	settingsPanelWidth = serializer->ReadFloat();
	int channelSettingsCount = serializer->ReadInt();
	for (int i = 0; i < channelSettingsCount; i++)
		channelHeights->ReplaceAt(serializer->ReadInt(), i);
}<commit_msg>Improved some awkward code in SeqUISequencer.<commit_after>#include <imgui.h>
#include <imgui_internal.h>
#include "SeqUISequencer.h";
#include "SeqProjectHeaders.h";
#include "SeqSerializer.h";
#include "SeqString.h";
#include "SeqList.h";

SeqUISequencer::SeqUISequencer(SeqProject *project):
	project(project)
{
	Init();
}

SeqUISequencer::SeqUISequencer(SeqProject *project, SeqSerializer *serializer):
	project(project)
{
	Init();
	Deserialize(serializer);
}

void SeqUISequencer::Init()
{
	// alloc memory
	name = SeqString::Format("Sequencer##%d", project->NextWindowId());
	channelHeights = new SeqList<int>();
	for (int i = 0; i < project->GetChannelCount(); i++)
		channelHeights->Add(initialChannelHeight);
	// start listening for project changes
	project->AddActionHandler(this);
}

SeqUISequencer::~SeqUISequencer()
{
	// stop listening for project changes
	project->RemoveActionHandler(this);
	// free memory
	delete[] name;
	delete channelHeights;
}

void SeqUISequencer::ActionDone(const SeqAction action)
{
	switch (action.type)
	{
		case SeqActionType::AddChannel:
			channelHeights->Add(initialChannelHeight);
			break;
	}
}

void SeqUISequencer::ActionUndone(const SeqAction action)
{
	switch (action.type)
	{
		case SeqActionType::AddChannel:
			channelHeights->RemoveAt(channelHeights->Count() - 1);
			break;
	}
}

SeqWindowType SeqUISequencer::GetWindowType()
{
	return SeqWindowType::Sequencer;
}

void SeqUISequencer::Draw()
{
	bool isWindowNew = false;
	if (!ImGui::FindWindowByName(name))
		isWindowNew = true;
	bool isOpen = true;

	if (ImGui::Begin(name, &isOpen, ImGuiWindowFlags_::ImGuiWindowFlags_NoScrollbar))
	{
		const float headerHeight = ImGui::GetTextLineHeightWithSpacing();
		ImGui::Columns(2, "panel");
		DrawChannelSettings(headerHeight, isWindowNew);
		ImGui::NextColumn();
		ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() - 4, ImGui::GetCursorPosY() + 1));
		DrawSequencerRuler(headerHeight);
		DrawChannels();
		ImGui::Columns(1);
	}
	ImGui::End();
	if (!isOpen)
		project->RemoveWindow(this);
}

void SeqUISequencer::DrawChannelSettings(float rulerHeight, bool isWindowNew)
{
	const ImGuiStyle style = ImGui::GetStyle();
	const ImVec2 origin = ImGui::GetCursorScreenPos();
	const ImVec2 size = ImVec2(settingsPanelWidth, ImGui::GetContentRegionAvail().y);

	// draw channel settings
	ImDrawList* drawList = ImGui::GetWindowDrawList();
	const ImU32 color = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_TextDisabled]);
	const ImU32 bgColor = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_ComboBg]);

	// default panel width
	if (isWindowNew)
		ImGui::SetColumnOffset(1, settingsPanelWidth + 7);

	// debug text
	ImGui::Text("%s", name);

	ImVec2 cursor = origin;
	cursor.y += rulerHeight + 1;
	ImGui::PushClipRect(cursor, ImVec2(cursor.x + size.x, cursor.y + size.y - rulerHeight - style.ScrollbarSize), false);
	// draw settings panels
	for (int i = 0; i < project->GetChannelCount(); i++)
	{
		float channelHeight = channelHeights->Get(i);
		// only draw if visible
		if (cursor.y - origin.y + channelHeight >= scrollY &&
			cursor.y - origin.y < scrollY + size.y)
		{
			drawList->AddRectFilled(ImVec2(cursor.x, cursor.y - scrollY), ImVec2(cursor.x + size.x, cursor.y - scrollY + channelHeight), bgColor, rounding, 0x1 | 0x8);
			drawList->AddRect(ImVec2(cursor.x, cursor.y - scrollY), ImVec2(cursor.x + size.x, cursor.y - scrollY + channelHeight), color, rounding, 0x1 | 0x8, lineThickness);
			ImGui::SetCursorScreenPos(ImVec2(cursor.x + 4, cursor.y - scrollY + 4));
			ImGui::Text("%s %d", project->GetChannel(i).name, i);
		}
		cursor.y += channelHeight + channelVerticalSpacing;
	}
	// draw and handle row resizing
	// code based on ImGui::Column()
	cursor = origin;
	cursor.y += rulerHeight;
	ImGuiContext* g = ImGui::GetCurrentContext();
	for (int i = 0; i < project->GetChannelCount(); i++)
	{
		float channelHeight = channelHeights->Get(i);
		cursor.y += channelHeight;
		// if visible
		if (cursor.y - origin.y >= scrollY &&
			cursor.y - origin.y < scrollY + size.y)
		{
			const ImGuiID row_id = ImGui::GetID("row") + ImGuiID(i);
			ImGui::KeepAliveID(row_id);
			const ImRect row_rect(ImVec2(cursor.x, cursor.y - scrollY - 4), ImVec2(cursor.x + size.x, cursor.y - scrollY + 4));

			bool hovered, held;
			ImGui::ButtonBehavior(row_rect, row_id, &hovered, &held);
			if (hovered || held)
				g->MouseCursor = ImGuiMouseCursor_ResizeNS;

			// Draw before resize so our items positioning are in sync with the line being drawn
			const ImU32 col = ImGui::GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
			drawList->AddLine(ImVec2(cursor.x + rounding, cursor.y - scrollY), ImVec2(cursor.x + size.x, cursor.y - scrollY), col);

			if (held)
			{
				if (g->ActiveIdIsJustActivated)
					g->ActiveIdClickOffset.y -= 4;   // Store from center of row line (we used a 8 high rect for row clicking)

				ImGuiWindow* window = ImGui::GetCurrentWindowRead();
				float newHeight = ImClamp(g->IO.MousePos.y - (cursor.y - channelHeight - scrollY) - g->ActiveIdClickOffset.y, minChannelHeight, maxChannelHeight);
				channelHeights->Set(i, newHeight);
			}
		}
		cursor.y += channelVerticalSpacing;
	}
	ImGui::PopClipRect();
	// clamp width
	settingsPanelWidth = ImGui::GetColumnOffset(1) - 7;
	if (settingsPanelWidth < minSettingsPanelWidth)
	{
		settingsPanelWidth = minSettingsPanelWidth;
		ImGui::SetColumnOffset(1, settingsPanelWidth);
	}
	if (settingsPanelWidth > maxSettingsPanelWidth)
	{
		settingsPanelWidth = maxSettingsPanelWidth;
		ImGui::SetColumnOffset(1, settingsPanelWidth);
	}
}

void SeqUISequencer::DrawSequencerRuler(float height)
{
	const ImGuiStyle style = ImGui::GetStyle();

	const ImVec2 origin = ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
	const float width = ImGui::GetContentRegionAvailWidth() - style.ScrollbarSize;
	const float thickness = 1.0f;
	const ImU32 color = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_TextDisabled]);

	// clip so we won't draw on top of the settings panel
	ImGui::PushClipRect(origin, ImVec2(origin.x + width, origin.y + height), false);

	ImDrawList* drawList = ImGui::GetWindowDrawList();
	// underline
	drawList->AddLine(ImVec2(origin.x, origin.y + height - 2), ImVec2(origin.x + width, origin.y + height - 2), color, thickness);

	// seconds lines
	int seconds = ceil(position);
	float firstOffset = -(position - floor(position)) * pixelsPerSecond / zoom;
	float scaledWidth = (origin.x + width) / zoom;
	float scaledStep = pixelsPerSecond / zoom;
	for (float x = origin.x + firstOffset; x < origin.x + scaledWidth; x += scaledStep)
	{
		drawList->AddLine(ImVec2(x, origin.y), ImVec2(x, origin.y + height - 1), color, thickness);
		ImGui::SetCursorScreenPos(ImVec2(x + style.FramePadding.x, origin.y));
		ImGui::Text("%02d\:%02d\.%d", (seconds - (seconds % 60)), seconds % 60, 0);
		seconds++;
	}
	ImGui::PopClipRect();

	// setup the position for channel drawing
	ImGui::SetCursorScreenPos(ImVec2(origin.x, origin.y + height));
}

void SeqUISequencer::DrawChannels()
{
	const ImGuiStyle style = ImGui::GetStyle();
	const ImVec2 contentSize = ImVec2(project->GetLength() * pixelsPerSecond / zoom, TotalChannelHeight() + 100);
	const ImVec2 size = ImVec2(ImGui::GetContentRegionAvail().x - style.ScrollbarSize, ImGui::GetContentRegionAvail().y - style.ScrollbarSize);
	ImGui::SetNextWindowContentSize(contentSize);
	ImGui::BeginChild("channels", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
	{
		position = ImGui::GetScrollX() / pixelsPerSecond * zoom;
		scrollY = ImGui::GetScrollY();

		const ImVec2 origin = ImGui::GetCursorScreenPos();

		ImDrawList* drawList = ImGui::GetWindowDrawList();

		const ImU32 color = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_TextDisabled]);
		const ImU32 bgColor = ImGui::ColorConvertFloat4ToU32(style.Colors[ImGuiCol_::ImGuiCol_ComboBg]);

		ImVec2 cursor = origin;
		for (int i = 0; i < project->GetChannelCount(); i++)
		{
			float channelHeight = channelHeights->Get(i);
			// only draw if visible
			if (cursor.y - origin.y + channelHeight >= scrollY &&
				cursor.y - origin.y < scrollY + size.y)
			{
				drawList->AddRectFilled(ImVec2(cursor.x + ImGui::GetScrollX(), cursor.y), ImVec2(cursor.x + contentSize.x, cursor.y + channelHeight), bgColor, rounding, 0x2 | 0x4);
				drawList->AddRect(ImVec2(cursor.x, cursor.y), ImVec2(cursor.x + contentSize.x, cursor.y + channelHeight), color, rounding, 0x2 | 0x4, lineThickness);
				//ImGui::SetCursorScreenPos(ImVec2(cursor.x + ImGui::GetScrollX() + 4, cursor.y + 4));
				//ImGui::Text("%s %d", project->GetChannel(i).name, i);
			}
			cursor.y += channelHeight + channelVerticalSpacing;
		}

		static ImVec2 scrollPosTextSize = ImGui::CalcTextSize("ScrollPos: 00000.000, 00000.000");
		ImGui::SetCursorPos(ImVec2(ImGui::GetScrollX() + size.x - scrollPosTextSize.x, ImGui::GetScrollY() + size.y - scrollPosTextSize.y));
		ImGui::Text("ScrollPos: %.3f, %.3f", ImGui::GetScrollX(), ImGui::GetScrollY());
	}
	ImGui::EndChild();
}

int SeqUISequencer::TotalChannelHeight()
{
	int totalHeight = 0;
	for (int i = 0; i < channelHeights->Count(); i++)
		totalHeight += channelHeights->Get(i) + channelVerticalSpacing;
	// remove spacing on the bottom most channel
	totalHeight -= channelVerticalSpacing;
	return totalHeight;
}

void SeqUISequencer::Serialize(SeqSerializer *serializer)
{
	serializer->Write(position);
	serializer->Write(zoom);
	serializer->Write(scrollY);
	serializer->Write(settingsPanelWidth);
	serializer->Write(channelHeights->Count());
	for (int i = 0; i < channelHeights->Count(); i++)
		serializer->Write(channelHeights->Get(i));
}

void SeqUISequencer::Deserialize(SeqSerializer *serializer)
{
	position = serializer->ReadDouble();
	zoom = serializer->ReadFloat();
	scrollY = serializer->ReadFloat();
	settingsPanelWidth = serializer->ReadFloat();
	int channelSettingsCount = serializer->ReadInt();
	for (int i = 0; i < channelSettingsCount; i++)
		channelHeights->ReplaceAt(serializer->ReadInt(), i);
}<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/thread.hpp"
#include "libtorrent/time.hpp"
#include <boost/tuple/tuple.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

void test_swarm(bool super_seeding = false, bool strict = false, bool seed_mode = false, bool time_critical = false)
{
	using namespace libtorrent;

	// in case the previous run was terminated
	error_code ec;
	remove_all("./tmp1_swarm", ec);
	remove_all("./tmp2_swarm", ec);
	remove_all("./tmp3_swarm", ec);

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000), "0.0.0.0", 0);
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50000, 51000), "0.0.0.0", 0);

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 100000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses2.set_download_rate_limit(int(rate_limit));
	ses3.set_download_rate_limit(int(rate_limit));
	ses2.set_upload_rate_limit(int(rate_limit / 2));
	ses3.set_upload_rate_limit(int(rate_limit / 2));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	settings.strict_super_seeding = strict;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	add_torrent_params p;
	p.seed_mode = seed_mode;
	// test using piece sizes smaller than 16kB
	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true
		, false, true, "_swarm", 32 * 1024, 0, super_seeding, &p);

	int mask = alert::all_categories & ~(alert::progress_notification | alert::performance_warning);
	ses1.set_alert_mask(mask);
	ses2.set_alert_mask(mask);
	ses3.set_alert_mask(mask);

	if (time_critical)
	{
		tor2.set_piece_deadline(2, seconds(0));
		tor2.set_piece_deadline(5, seconds(1));
		tor2.set_piece_deadline(8, seconds(2));
	}

	float sum_dl_rate2 = 0.f;
	float sum_dl_rate3 = 0.f;
	int count_dl_rates2 = 0;
	int count_dl_rates3 = 0;

	for (int i = 0; i < 80; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");
		print_alerts(ses3, "ses3");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		if (st2.progress < 1.f && st2.progress > 0.5f)
		{
			sum_dl_rate2 += st2.download_payload_rate;
			++count_dl_rates2;
		}
		if (st3.progress < 1.f && st3.progress > 0.5f)
		{
			sum_dl_rate3 += st3.download_rate;
			++count_dl_rates3;
		}

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		if (tor2.is_seed() && tor3.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
	TEST_CHECK(tor3.is_seed());

	float average2 = sum_dl_rate2 / float(count_dl_rates2);
	float average3 = sum_dl_rate3 / float(count_dl_rates3);

	std::cerr << average2 << std::endl;
	std::cerr << "average rate: " << (average2 / 1000.f) << "kB/s - "
		<< (average3 / 1000.f) << "kB/s" << std::endl;

	if (tor2.is_seed() && tor3.is_seed()) std::cerr << "done\n";

	// make sure the files are deleted
	ses1.remove_torrent(tor1, session::delete_files);
	ses2.remove_torrent(tor2, session::delete_files);
	ses3.remove_torrent(tor3, session::delete_files);

	std::auto_ptr<alert> a = ses1.pop_alert();
	ptime end = time_now() + seconds(20);
	while (a.get() == 0 || dynamic_cast<torrent_deleted_alert*>(a.get()) == 0)
	{
		if (ses1.wait_for_alert(end - time_now()) == 0)
		{
			std::cerr << "wait_for_alert() expired" << std::endl;
			break;
		}
		a = ses1.pop_alert();
		assert(a.get());
		std::cerr << a->message() << std::endl;
	}

	TEST_CHECK(dynamic_cast<torrent_deleted_alert*>(a.get()) != 0);

	// there shouldn't be any alerts generated from now on
	// make sure that the timer in wait_for_alert() works
	// this should time out (ret == 0) and it should take
	// about 2 seconds
	ptime start = time_now_hires();
	alert const* ret;
	while ((ret = ses1.wait_for_alert(seconds(2))))
	{
		a = ses1.pop_alert();
		std::cerr << ret->message() << std::endl;
		start = time_now();
	}
	TEST_CHECK(time_now_hires() - start < seconds(3));
	TEST_CHECK(time_now_hires() - start >= seconds(2));

	TEST_CHECK(!exists("./tmp1_swarm/temporary"));
	TEST_CHECK(!exists("./tmp2_swarm/temporary"));
	TEST_CHECK(!exists("./tmp3_swarm/temporary"));

	remove_all("./tmp1_swarm", ec);
	remove_all("./tmp2_swarm", ec);
	remove_all("./tmp3_swarm", ec);
}

int test_main()
{
	using namespace libtorrent;

	// with time critical pieces
	test_swarm(false, false, false, true);

	// with seed mode
	test_swarm(false, false, true);

	test_swarm();

	// with super seeding
	test_swarm(true);

	// with strict super seeding
	test_swarm(true, true);

	return 0;
}

<commit_msg>fixed test_swarm build<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/thread.hpp"
#include "libtorrent/time.hpp"
#include <boost/tuple/tuple.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

void test_swarm(bool super_seeding = false, bool strict = false, bool seed_mode = false, bool time_critical = false)
{
	using namespace libtorrent;

	// in case the previous run was terminated
	error_code ec;
	remove_all("./tmp1_swarm", ec);
	remove_all("./tmp2_swarm", ec);
	remove_all("./tmp3_swarm", ec);

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000), "0.0.0.0", 0);
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50000, 51000), "0.0.0.0", 0);

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 100000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses2.set_download_rate_limit(int(rate_limit));
	ses3.set_download_rate_limit(int(rate_limit));
	ses2.set_upload_rate_limit(int(rate_limit / 2));
	ses3.set_upload_rate_limit(int(rate_limit / 2));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	settings.strict_super_seeding = strict;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	add_torrent_params p;
	p.seed_mode = seed_mode;
	// test using piece sizes smaller than 16kB
	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true
		, false, true, "_swarm", 32 * 1024, 0, super_seeding, &p);

	int mask = alert::all_categories & ~(alert::progress_notification | alert::performance_warning);
	ses1.set_alert_mask(mask);
	ses2.set_alert_mask(mask);
	ses3.set_alert_mask(mask);

	if (time_critical)
	{
		tor2.set_piece_deadline(2, 0);
		tor2.set_piece_deadline(5, 1000);
		tor2.set_piece_deadline(8, 2000);
	}

	float sum_dl_rate2 = 0.f;
	float sum_dl_rate3 = 0.f;
	int count_dl_rates2 = 0;
	int count_dl_rates3 = 0;

	for (int i = 0; i < 80; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");
		print_alerts(ses3, "ses3");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		if (st2.progress < 1.f && st2.progress > 0.5f)
		{
			sum_dl_rate2 += st2.download_payload_rate;
			++count_dl_rates2;
		}
		if (st3.progress < 1.f && st3.progress > 0.5f)
		{
			sum_dl_rate3 += st3.download_rate;
			++count_dl_rates3;
		}

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		if (tor2.is_seed() && tor3.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
	TEST_CHECK(tor3.is_seed());

	float average2 = sum_dl_rate2 / float(count_dl_rates2);
	float average3 = sum_dl_rate3 / float(count_dl_rates3);

	std::cerr << average2 << std::endl;
	std::cerr << "average rate: " << (average2 / 1000.f) << "kB/s - "
		<< (average3 / 1000.f) << "kB/s" << std::endl;

	if (tor2.is_seed() && tor3.is_seed()) std::cerr << "done\n";

	// make sure the files are deleted
	ses1.remove_torrent(tor1, session::delete_files);
	ses2.remove_torrent(tor2, session::delete_files);
	ses3.remove_torrent(tor3, session::delete_files);

	std::auto_ptr<alert> a = ses1.pop_alert();
	ptime end = time_now() + seconds(20);
	while (a.get() == 0 || dynamic_cast<torrent_deleted_alert*>(a.get()) == 0)
	{
		if (ses1.wait_for_alert(end - time_now()) == 0)
		{
			std::cerr << "wait_for_alert() expired" << std::endl;
			break;
		}
		a = ses1.pop_alert();
		assert(a.get());
		std::cerr << a->message() << std::endl;
	}

	TEST_CHECK(dynamic_cast<torrent_deleted_alert*>(a.get()) != 0);

	// there shouldn't be any alerts generated from now on
	// make sure that the timer in wait_for_alert() works
	// this should time out (ret == 0) and it should take
	// about 2 seconds
	ptime start = time_now_hires();
	alert const* ret;
	while ((ret = ses1.wait_for_alert(seconds(2))))
	{
		a = ses1.pop_alert();
		std::cerr << ret->message() << std::endl;
		start = time_now();
	}
	TEST_CHECK(time_now_hires() - start < seconds(3));
	TEST_CHECK(time_now_hires() - start >= seconds(2));

	TEST_CHECK(!exists("./tmp1_swarm/temporary"));
	TEST_CHECK(!exists("./tmp2_swarm/temporary"));
	TEST_CHECK(!exists("./tmp3_swarm/temporary"));

	remove_all("./tmp1_swarm", ec);
	remove_all("./tmp2_swarm", ec);
	remove_all("./tmp3_swarm", ec);
}

int test_main()
{
	using namespace libtorrent;

	// with time critical pieces
	test_swarm(false, false, false, true);

	// with seed mode
	test_swarm(false, false, true);

	test_swarm();

	// with super seeding
	test_swarm(true);

	// with strict super seeding
	test_swarm(true, true);

	return 0;
}

<|endoftext|>


<commit_before>#ifndef DOWNURL_CPP
#define DOWNURL_CPP
#include "header.h"
#include "class_struct.h"
#include "FUNCTIONS.h"

HTML_DOWN::HTML_DOWN(){}

HTML_DOWN:: ~HTML_DOWN(){}

string HTML_DOWN::download(string url)
{
    curl = curl_easy_init();
	std::stringstream out;
	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
	CURLcode res = curl_easy_perform(curl);
	if (res != CURLE_OK)
	{
	   cout<<stderr<<"UNABLE TO ACCESS "<<url<<" "<<curl_easy_strerror(res)<<"\n";
	}
	curl_easy_cleanup(curl);
	return out.str();
}

void HTML_DOWN::save_down(string url,string file)
{
	cout<<file<<"     ____FILE____\n";
	FILE* fp = fopen(file.c_str(), "wb");
	if (!fp)
	{
	    printf("!!! Failed to create file \n");
		exit(-1);
	}
	cout<<"WRITING DATA TO FILE\n";
	curl = curl_easy_init();
	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_file);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
	CURLcode res = curl_easy_perform(curl);
	if (res != CURLE_OK)
	{
	    cout<<stderr<<"UNABLE TO ACCESS "<<url<<" "<<curl_easy_strerror(res)<<"\n";
	}
	curl_easy_cleanup(curl);
	fclose(fp);
}

hp HTML_DOWN::path_to_save(string url)
{
	hp temp;
	size_t found = url.find("//");
	size_t found2 = url.find_last_of("/");
	if(found2!=std::string::npos){
	if(found!=std::string::npos) temp.host =  url.substr(found+2,found2);
	temp.host =  url.substr(0,found2);
	temp.path =  url.substr(found2+1);
	//else temp.path = "NOT_FOUND_PATH_NOFILE";
	found = temp.path.find_last_of(".");
	if(found ==std::string::npos){
		temp.host.append("/").append(temp.path);
		 temp.path = ("/_index.html");
	}
	}
	else{
		temp.host =  url;
		temp.path = "NO_PATH.html";
	}
	return temp;
}

void HTML_DOWN::save(string url)
{
	ofstream myfile;
	string tp = "mkdir -p ";
	hp t = path_to_save(url);
	if(t.path.size() >2)
	{
		tp.append(t.host);
		system(tp.c_str());
		save_down(url,t.host.append("/").append(t.path));
	}
}

void HTML_DOWN::rm_getvar(string *s)
{
	size_t found1 = s->find("href=\"");
	if(found1!=std::string::npos)
		s->assign(s->substr(found1+6));
	int n = s->size();
	string st="";
	for(int i=0;i<n;i++){
		if(s->at(i) !=' ' && s->at(i) !='\n' && s->at(i) != '\t'){
			if(s->at(i) =='?')break;
			if(s->at(i) =='"')break;
			if(s->at(i) =='#')break;
			st.push_back(s->at(i));
		}
	}
	if(st.size() ==0)st="/";
	s->assign(st);
}

void HTML_DOWN::is_dir(string *s)
{

}
void HTML_DOWN::url_res(string *s)
{
	size_t found = s->find("//");
	if(found!=std::string::npos)
	{
		s->assign(s->substr(found+2));
	}
}

hp HTML_DOWN::host_path(string *s, string curr_url)
{
	hp temp;
	temp.host="";
	temp.host="";
	size_t path =std::string::npos;
	size_t found = s->find("//");
	if(found!=std::string::npos)
	{
		s->assign(s->substr(found+2));
		int n = s->size();
		for(int i=0;i<n;++i)
		{
			if(s->at(i) =='/'){ path =i;break;}
			temp.host.push_back(s->at(i));
		}
		if(path !=std::string::npos)
			temp.path = s->substr(path);
		else  temp.path ="/";
		return temp;
	}
	else
	{
		url_res(&curr_url);
		int n = curr_url.size();
		size_t t2 =std::string::npos;
		for(int i=0;i<n;++i)
		{
			if(curr_url.at(i) =='/'){ path =i;break;}
			temp.host.push_back(curr_url.at(i));
		}
		if(path!=std::string::npos)
		{
			for(int i=n-1;i >=0;i--)
			{
				if(curr_url.at(i) =='/'){t2 =i;break;}
			}
			if(t2!=std::string::npos)
			{
				for(int i=path;i<=t2;++i)
				{
					temp.path.push_back(curr_url.at(i));
				}
				if(s->at(0) == '/')
				{
					if(s->size()>1)
						temp.path.assign(s->substr(0));
				}
				else temp.path.append(s->substr(0));
				return temp;
			}
		}
		else
		{
			if(s->at(0) == '/')
			temp.path.append((*s));
			else temp.path.append("/").append((*s));
			return temp;
		}
	}
	return temp;
}
vector<hp> HTML_DOWN::get_links(string s,string url)
{
	vector<hp> temp;
	std::regex word_regex("\\s*?href\\s*=\\s*\"[^\"]*\"");
	auto words_begin =	std::sregex_iterator(s.begin(), s.end(), word_regex);
	auto words_end = std::sregex_iterator();
	for (std::sregex_iterator i = words_begin; i != words_end; ++i)
	{
		std::smatch match = *i;
		std::string match_str = match.str();
		if(match_str.size()>1)
		{
			rm_getvar(&match_str);
			hp test = host_path(&match_str,url);
			temp.push_back(test);
		}
	}
	s.clear();
    return temp;
}

URLS::URLS(){;}
URLS::~URLS(){;};
void URLS::add_path(string path,domain_ext *itt )
{
	std::map<string,time_t>::iterator its;
	its = ((itt)->path).find(path);
	if(its != (itt)->path.end())
	{
		//found
	}
	else{
		//(itt)->PATH_WRITE_MTX.lock();
		its = ((itt)->path).find(path);
		if(its != (itt)->path.end())
		{
			//found
			//(itt)->PATH_WRITE_MTX.unlock();
		}
		else
		{
			time_t tl = time(0);
			(itt)->path.insert(std::pair<string,time_t>(path,tl));
			(itt)->lt = time(0);
			//(itt)->PATH_WRITE_MTX.unlock();
		}
	}
}

void URLS::add_url(string domain,string path,time_t temp_time)
{ 	//to add url if not exists
	std::map<string,domain_ext>::iterator it;
	it = urls.find(domain);
	if (it != urls.end())
	{
		add_path(path,&(it->second));
	}
	else
	{    //if domain not found
		DOMAIN_WRITE_MTX.lock();
		it = urls.find(domain);
		if (it != urls.end())
		{
			DOMAIN_WRITE_MTX.unlock();   //domain found recent entry
			add_path(path,&(it->second));
		}
		else
		{
			map<string,time_t> t;
			t.insert(std::pair<string,time_t>(path,time(0)));
			domain_ext temp ;
			temp.path = t;
			temp.lt = temp_time;
			urls.insert(std::make_pair(domain,temp));
			DOMAIN_WRITE_MTX.unlock();
		}
	}
}

void URLS::get_path(domain_ext *ext,string host)
{
	for (std::map<string,time_t>::iterator it=ext->path.begin(); it!=ext->path.end(); ++it)
	std::cout << "http://"<<host<<it->first << "   => " << it->second << '\n';
}

void URLS::get_domain()
{
	for (std::map<string,domain_ext>::iterator it=urls.begin(); it!=urls.end(); ++it)
	{
		//std::cout << it->first << " => ";
		get_path(&(it->second),it->first);
	}
}

Ctrler::Ctrler(URLS *url)
{
	urls = url;
}

Ctrler::Ctrler(){};

void Ctrler::toask(string url)
{
	vector<hp> hp1 = htm.get_links(htm.download(url),url);

	int size = hp1.size();
	for(int i=0;i<size;++i)
	{
		urls->add_url(hp1.at(i).host,hp1.at(i).path,0);
	}
	hp1.clear();
}

void Ctrler::dotask(map<string,domain_ext>::iterator  *itp)
{
	(*itp)->second.lt =time(0);
	time_t max =time(0);

	std::map<string,time_t>::iterator its= (*itp)->second.path.begin();
	for (std::map<string,time_t>::iterator it=(*itp)->second.path.begin(); it!=(*itp)->second.path.end(); ++it)
		if((it)->second < max)its = it;

	string temp_url = (*itp)->first;
	temp_url.append((its)->first);
	s =htm.download(temp_url);
	vector<hp> hp1 = htm.get_links(s,temp_url);
	s="";
	int size = hp1.size();
	for(int i=0;i<size;++i)
	{
		urls->add_url(hp1.at(i).host,hp1.at(i).path,0);
	}
	hp1.clear();
	(*itp)->second.lt =time(0);
}

std::map<string,time_t>::iterator* Ctrler::_last(map<string,domain_ext>::iterator  *itp)
{
	domain_ext *ext = &(itn)->second;
	time_t max =time(0);
	std::map<string,time_t>::iterator *its;
	for (std::map<string,time_t>::iterator it=(ext)->path.begin(); it!=(ext)->path.end(); ++it)
		if((it)->second < max)its =
		return its;
}

void Ctrler::nexttask()
{
	int t=1;
	for(itn =urls->urls.begin() ;(itn) !=urls->urls.end();itn++)
	{
		if(difftime(time(0),(itn)->second.lt) > 10000)
		{
			//cin>>t;
			if(t==1)
				dotask(&itn);
			else if(t==5)break;
		}
	}
}

void Ctrler::save_help(map<string,domain_ext>::iterator  *itp)
{
	(*itp)->second.lt =time(0);
	time_t max =time(0);
	std::map<string,time_t>::iterator its= (*itp)->second.path.begin();
	for (std::map<string,time_t>::iterator it=(*itp)->second.path.begin(); it!=(*itp)->second.path.end(); ++it){
		if((it)->second < max)its = it;
	string temp_url = (*itp)->first;
	temp_url.append((its)->first);
	htm.save(temp_url);
	}
}

void Ctrler::save()
{
	for(itn =urls->urls.begin() ;(itn) !=urls->urls.end();itn++)
	{
		//if(difftime(time(0),(itn)->second.lt) > 10){
		//cin>>t;
		save_help(&itn);

		//}
	}
}

#endif

<commit_msg>update: pathfix for dir and same file , added _index.html<commit_after>#ifndef DOWNURL_CPP
#define DOWNURL_CPP
#include "header.h"
#include "class_struct.h"
#include "FUNCTIONS.h"

HTML_DOWN::HTML_DOWN() {}

HTML_DOWN::~HTML_DOWN() {}

string HTML_DOWN::download(string url)
{
	curl = curl_easy_init();
	std::stringstream out;
	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
	CURLcode res = curl_easy_perform(curl);
	if (res != CURLE_OK)
	{
		cout << stderr << "UNABLE TO ACCESS " << url << " " << curl_easy_strerror(res) << "\n";
	}
	curl_easy_cleanup(curl);
	return out.str();
}

void HTML_DOWN::save_down(string url, string file)
{
	cout << file << "     ____FILE____\n";
	FILE *fp = fopen(file.c_str(), "wb");
	if (!fp)
	{
		printf("!!! Failed to create file \n");
		exit(-1);
	}
	cout << "WRITING DATA TO FILE\n";
	curl = curl_easy_init();
	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_file);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
	CURLcode res = curl_easy_perform(curl);
	if (res != CURLE_OK)
	{
		cout << stderr << "UNABLE TO ACCESS " << url << " " << curl_easy_strerror(res) << "\n";
	}
	curl_easy_cleanup(curl);
	fclose(fp);
}

hp HTML_DOWN::path_to_save(string url)
{
	hp temp;
	size_t found = url.find("//");
	size_t found2 = url.find_last_of("/");
	if (found2 != std::string::npos)
	{
		if (found != std::string::npos)
			temp.host = url.substr(found + 2, found2);
		temp.host = url.substr(0, found2);
		temp.path = url.substr(found2 + 1);
		//else temp.path = "NOT_FOUND_PATH_NOFILE";
		found = temp.path.find_last_of(".");
		if (found == std::string::npos)
		{
			temp.host.append("/").append(temp.path);
			temp.path = ("/_index.html");
		}
	}
	else
	{
		temp.host = url;
		temp.path = "NO_PATH.html";
	}
	return temp;
}

void HTML_DOWN::save(string url)
{
	ofstream myfile;
	string tp = "mkdir -p ";
	hp t = path_to_save(url);
	if (t.path.size() > 2)
	{
		tp.append(t.host);
		system(tp.c_str());
		save_down(url, t.host.append("/").append(t.path));
	}
}

void HTML_DOWN::rm_getvar(string *s)
{
	size_t found1 = s->find("href=\"");
	if (found1 != std::string::npos)
		s->assign(s->substr(found1 + 6));
	int n = s->size();
	string st = "";
	for (int i = 0; i < n; i++)
	{
		if (s->at(i) != ' ' && s->at(i) != '\n' && s->at(i) != '\t')
		{
			if (s->at(i) == '?')
				break;
			if (s->at(i) == '"')
				break;
			if (s->at(i) == '#')
				break;
			st.push_back(s->at(i));
		}
	}
	if (st.size() == 0)
		st = "/";
	s->assign(st);
}

void HTML_DOWN::is_dir(string *s)
{
}
void HTML_DOWN::url_res(string *s)
{
	size_t found = s->find("//");
	if (found != std::string::npos)
	{
		s->assign(s->substr(found + 2));
	}
}

hp HTML_DOWN::host_path(string *s, string curr_url)
{
	hp temp;
	temp.host = "";
	temp.host = "";
	size_t path = std::string::npos;
	size_t found = s->find("//");
	if (found != std::string::npos)
	{
		s->assign(s->substr(found + 2));
		int n = s->size();
		for (int i = 0; i < n; ++i)
		{
			if (s->at(i) == '/')
			{
				path = i;
				break;
			}
			temp.host.push_back(s->at(i));
		}
		if (path != std::string::npos)
			temp.path = s->substr(path);
		else
			temp.path = "/";
		return temp;
	}
	else
	{
		url_res(&curr_url);
		int n = curr_url.size();
		size_t t2 = std::string::npos;
		for (int i = 0; i < n; ++i)
		{
			if (curr_url.at(i) == '/')
			{
				path = i;
				break;
			}
			temp.host.push_back(curr_url.at(i));
		}
		if (path != std::string::npos)
		{
			for (int i = n - 1; i >= 0; i--)
			{
				if (curr_url.at(i) == '/')
				{
					t2 = i;
					break;
				}
			}
			if (t2 != std::string::npos)
			{
				for (int i = path; i <= t2; ++i)
				{
					temp.path.push_back(curr_url.at(i));
				}
				if (s->at(0) == '/')
				{
					if (s->size() > 1)
						temp.path.assign(s->substr(0));
				}
				else
					temp.path.append(s->substr(0));
				return temp;
			}
		}
		else
		{
			if (s->at(0) == '/')
				temp.path.append((*s));
			else
				temp.path.append("/").append((*s));
			return temp;
		}
	}
	return temp;
}
vector<hp> HTML_DOWN::get_links(string s, string url)
{
	vector<hp> temp;
	std::regex word_regex("\\s*?href\\s*=\\s*\"[^\"]*\"");
	auto words_begin = std::sregex_iterator(s.begin(), s.end(), word_regex);
	auto words_end = std::sregex_iterator();
	for (std::sregex_iterator i = words_begin; i != words_end; ++i)
	{
		std::smatch match = *i;
		std::string match_str = match.str();
		if (match_str.size() > 1)
		{
			rm_getvar(&match_str);
			hp test = host_path(&match_str, url);
			temp.push_back(test);
		}
	}
	s.clear();
	return temp;
}

URLS::URLS() { ; }
URLS::~URLS() { ; };
void URLS::add_path(string path, domain_ext *itt)
{
	std::map<string, time_t>::iterator its;
	its = ((itt)->path).find(path);
	if (its != (itt)->path.end())
	{
		//found
	}
	else
	{
		//(itt)->PATH_WRITE_MTX.lock();
		its = ((itt)->path).find(path);
		if (its != (itt)->path.end())
		{
			//found
			//(itt)->PATH_WRITE_MTX.unlock();
		}
		else
		{
			time_t tl = time(0);
			(itt)->path.insert(std::pair<string, time_t>(path, tl));
			(itt)->lt = time(0);
			//(itt)->PATH_WRITE_MTX.unlock();
		}
	}
}

void URLS::add_url(string domain, string path, time_t temp_time)
{ //to add url if not exists
	std::map<string, domain_ext>::iterator it;
	it = urls.find(domain);
	if (it != urls.end())
	{
		add_path(path, &(it->second));
	}
	else
	{ //if domain not found
		DOMAIN_WRITE_MTX.lock();
		it = urls.find(domain);
		if (it != urls.end())
		{
			DOMAIN_WRITE_MTX.unlock(); //domain found recent entry
			add_path(path, &(it->second));
		}
		else
		{
			map<string, time_t> t;
			t.insert(std::pair<string, time_t>(path, time(0)));
			domain_ext temp;
			temp.path = t;
			temp.lt = temp_time;
			urls.insert(std::make_pair(domain, temp));
			DOMAIN_WRITE_MTX.unlock();
		}
	}
}

void URLS::get_path(domain_ext *ext, string host)
{
	for (std::map<string, time_t>::iterator it = ext->path.begin(); it != ext->path.end(); ++it)
		std::cout << "http://" << host << it->first << "   => " << it->second << '\n';
}

void URLS::get_domain()
{
	for (std::map<string, domain_ext>::iterator it = urls.begin(); it != urls.end(); ++it)
	{
		//std::cout << it->first << " => ";
		get_path(&(it->second), it->first);
	}
}

Ctrler::Ctrler(URLS *url)
{
	urls = url;
}

Ctrler::Ctrler(){};

void Ctrler::toask(string url)
{
	vector<hp> hp1 = htm.get_links(htm.download(url), url);

	int size = hp1.size();
	for (int i = 0; i < size; ++i)
	{
		urls->add_url(hp1.at(i).host, hp1.at(i).path, 0);
	}
	hp1.clear();
}

void Ctrler::dotask(map<string, domain_ext>::iterator *itp)
{
	(*itp)->second.lt = time(0);
	time_t max = time(0);

	std::map<string, time_t>::iterator its = (*itp)->second.path.begin();
	for (std::map<string, time_t>::iterator it = (*itp)->second.path.begin(); it != (*itp)->second.path.end(); ++it)
		if ((it)->second < max)
			its = it;

	string temp_url = (*itp)->first;
	temp_url.append((its)->first);
	s = htm.download(temp_url);
	vector<hp> hp1 = htm.get_links(s, temp_url);
	s = "";
	int size = hp1.size();
	for (int i = 0; i < size; ++i)
	{
		urls->add_url(hp1.at(i).host, hp1.at(i).path, 0);
	}
	hp1.clear();
	(*itp)->second.lt = time(0);
}

std::map<string, time_t>::iterator *Ctrler::_last(map<string, domain_ext>::iterator *itp)
{
	domain_ext *ext = &(itn)->second;
	time_t max = time(0);
	std::map<string, time_t>::iterator *its;
	for (std::map<string, time_t>::iterator it = (ext)->path.begin(); it != (ext)->path.end(); ++it)
		if ((it)->second < max)
			its =
	return its;
}

void Ctrler::nexttask()
{
	int t = 1;
	for (itn = urls->urls.begin(); (itn) != urls->urls.end(); itn++)
	{
		if (difftime(time(0), (itn)->second.lt) > 10000)
		{
			//cin>>t;
			if (t == 1)
				dotask(&itn);
			else if (t == 5)
				break;
		}
	}
}

void Ctrler::save_help(map<string, domain_ext>::iterator *itp)
{
	(*itp)->second.lt = time(0);
	time_t max = time(0);
	std::map<string, time_t>::iterator its = (*itp)->second.path.begin();
	for (std::map<string, time_t>::iterator it = (*itp)->second.path.begin(); it != (*itp)->second.path.end(); ++it)
	{
		if ((it)->second < max)
			its = it;
		string temp_url = (*itp)->first;
		temp_url.append((its)->first);
		htm.save(temp_url);
	}
}

void Ctrler::save()
{
	for (itn = urls->urls.begin(); (itn) != urls->urls.end(); itn++)
	{
		//if(difftime(time(0),(itn)->second.lt) > 10){
		//cin>>t;
		save_help(&itn);

		//}
	}
}

#endif
<|endoftext|>


<commit_before>// http://www.apache.org/licenses/LICENSE-2.0
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#include "lua_bindings/util.h"
#include "core/log.h"
#include <cereal/archives/portable_binary.hpp>
#include <cereal/types/string.hpp>
#define MODULE "lua_bindings"

namespace lua_bindings {

/* Type format:
{"object",
    {"peer", "int32_t"},
    {"players", {"unordered_map",
        "int32_t",
        {"object",
            {"peer", "int32_t"},
            {"x", "int32_t"},
            {"y", "int32_t"},
        },
    }},
    {"playfield", {"object",
        {"w", "int32_t"},
        {"h", "int32_t"},
        {"tiles", {"array", "int32_t"}},
    }},
}) */

static constexpr auto known_types =
		"byte, int32_t, double, array, unordered_map, object";

// Places result value on top of stack
static void binary_input_read_value(lua_State *L, int type_L,
		cereal::PortableBinaryInputArchive &ar)
{
	if(type_L < 0) type_L = lua_gettop(L) + type_L + 1;

	// Read value definition
	bool has_table = false;
	ss_ outfield_type;
	if(lua_istable(L, type_L)){
		has_table = true;
		lua_rawgeti(L, type_L, 1);
		outfield_type = lua_tocppstring(L, -1);
		lua_pop(L, 1);
	} else if(lua_isstring(L, type_L)){
		outfield_type = lua_tocppstring(L, type_L);
	} else {
		throw Exception("Value definition table or string expected");
	}

	log_t(MODULE, "binary_input_read_value(): type=%s", cs(outfield_type));

	if(outfield_type == "byte"){
		uchar value;
		ar(value);
		log_t(MODULE, "byte value=%i", (int)value);
		lua_pushinteger(L, value);
		// value is left on stack
	} else if(outfield_type == "int32_t"){
		int32_t value;
		ar(value);
		log_t(MODULE, "int32_t value=%i", value);
		lua_pushinteger(L, value);
		// value is left on stack
	} else if(outfield_type == "double"){
		double value;
		ar(value);
		log_t(MODULE, "double value=%f", value);
		lua_pushnumber(L, value);
		// value is left on stack
	} else if(outfield_type == "string"){
		ss_ value;
		ar(value);
		log_t(MODULE, "string value=%s", cs(value));
		lua_pushlstring(L, value.c_str(), value.size());
		// value is left on stack
	} else if(outfield_type == "array"){
		if(!has_table)
			throw Exception("array requires parameter table");
		lua_newtable(L);
		int value_result_table_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 2);
		int array_type_L = lua_gettop(L);
		// Loop through array items
		uint64_t num_entries;
		ar(num_entries);
		for(uint64_t i = 0; i < num_entries; i++){
			log_t(MODULE, "array[%s]", cs(i));
			binary_input_read_value(L, array_type_L, ar);
			lua_rawseti(L, value_result_table_L, i + 1);
		}
		lua_pop(L, 1); // array_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "unordered_map"){
		if(!has_table)
			throw Exception("unordered_map requires parameter table");
		lua_newtable(L);
		int value_result_table_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 2);
		int map_key_type_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 3);
		int map_value_type_L = lua_gettop(L);
		// Loop through map entries
		uint64_t num_entries;
		ar(num_entries);
		for(uint64_t i = 0; i < num_entries; i++){
			log_t(MODULE, "unordered_map[%s]", cs(i));
			binary_input_read_value(L, map_key_type_L, ar);
			binary_input_read_value(L, map_value_type_L, ar);
			lua_rawset(L, value_result_table_L);
		}
		lua_pop(L, 1); // map_value_type_L
		lua_pop(L, 1); // map_key_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "object"){
		if(!has_table)
			throw Exception("object requires parameter table");
		lua_newtable(L);
		int value_result_table_L = lua_gettop(L);
		// Loop through object fields
		size_t field_i = 0;
		lua_pushnil(L);
		while(lua_next(L, type_L) != 0){
			if(field_i != 0){
				log_t(MODULE, "object field %zu", field_i);
				int field_def_L = lua_gettop(L);
				lua_rawgeti(L, field_def_L, 1); // name
				lua_rawgeti(L, field_def_L, 2); // type
				log_t(MODULE, " = object[\"%s\"]", lua_tostring(L, -2));
				binary_input_read_value(L, -1, ar); // Uses type, pushes value
				lua_remove(L, -2); // Remove type
				lua_rawset(L, value_result_table_L); // Set t[#-2] = #-1
			}
			lua_pop(L, 1); // Continue iterating by popping table value
			field_i++;
		}
		// value_result_table_L is left on stack
	} else {
		throw Exception(ss_()+"Unknown type \""+outfield_type+"\""
					  "; known types are "+known_types);
	}
}

static void binary_output_write_value(lua_State *L, int value_L, int type_L,
		cereal::PortableBinaryOutputArchive &ar)
{
	if(value_L < 0) value_L = lua_gettop(L) + value_L + 1;
	if(type_L < 0) type_L = lua_gettop(L) + type_L + 1;

	// Read value definition
	bool has_table = false;
	ss_ outfield_type;
	if(lua_istable(L, type_L)){
		has_table = true;
		lua_rawgeti(L, type_L, 1);
		outfield_type = lua_tocppstring(L, -1);
		lua_pop(L, 1);
	} else if(lua_isstring(L, type_L)){
		outfield_type = lua_tocppstring(L, type_L);
	} else {
		throw Exception("Value definition table or string expected");
	}

	log_t(MODULE, "binary_output_write_value(): type=%s", cs(outfield_type));

	if(outfield_type == "byte"){
		uchar value = lua_tointeger(L, value_L);
		log_t(MODULE, "byte value=%i", (int)value);
		ar(value);
	} else if(outfield_type == "int32_t"){
		int32_t value = lua_tointeger(L, value_L);
		log_t(MODULE, "int32_t value=%i", value);
		ar(value);
	} else if(outfield_type == "double"){
		double value = lua_tonumber(L, value_L);
		log_t(MODULE, "double value=%f", value);
		ar(value);
	} else if(outfield_type == "string"){
		ss_ value = lua_tocppstring(L, value_L);
		log_t(MODULE, "string value=%s", cs(value));
		ar(value);
	} else if(outfield_type == "array"){
		if(!has_table)
			throw Exception("array requires parameter table");
		lua_rawgeti(L, type_L, 2);
		int array_type_L = lua_gettop(L);
		// Loop through array items
		uint64_t num_entries = 0;
		lua_pushnil(L);
		while(lua_next(L, value_L) != 0){
			lua_pop(L, 1); // Continue iterating by popping table value
			num_entries++;
		}
		ar(num_entries);
		lua_pushnil(L);
		int i = 1;
		while(lua_next(L, value_L) != 0){
			log_t(MODULE, "array[%i]", i);
			binary_output_write_value(L, -1, array_type_L, ar);
			lua_pop(L, 1); // Continue iterating by popping table value
			i++;
		}
		lua_pop(L, 1); // array_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "unordered_map"){
		if(!has_table)
			throw Exception("unordered_map requires parameter table");
		lua_rawgeti(L, type_L, 2);
		int map_key_type_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 3);
		int map_value_type_L = lua_gettop(L);
		// Loop through map entries
		uint64_t num_entries = 0;
		lua_pushnil(L);
		while(lua_next(L, value_L) != 0){
			lua_pop(L, 1); // Continue iterating by popping table value
			num_entries++;
		}
		ar(num_entries);
		lua_pushnil(L);
		while(lua_next(L, value_L) != 0){
			int key_L = lua_gettop(L) - 1;
			int value_L = lua_gettop(L);
			log_t(MODULE, "unordered_map[%s]", lua_tostring(L, key_L));
			binary_output_write_value(L, key_L, map_key_type_L, ar);
			binary_output_write_value(L, value_L, map_value_type_L, ar);
			lua_pop(L, 1); // Continue iterating by popping table value
		}
		lua_pop(L, 1); // map_value_type_L
		lua_pop(L, 1); // map_key_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "object"){
		if(!has_table)
			throw Exception("object requires parameter table");
		// Loop through object fields
		size_t field_i = 0;
		lua_pushnil(L);
		while(lua_next(L, type_L) != 0){
			if(field_i != 0){
				log_t(MODULE, "object field %zu", field_i);
				int field_def_L = lua_gettop(L);
				lua_rawgeti(L, field_def_L, 2); // type
				lua_rawgeti(L, field_def_L, 1); // name
				log_t(MODULE, " = object[\"%s\"]", lua_tostring(L, -1));
				// Get value_L[name]; name is replaced by value
				lua_rawget(L, value_L);
				// Recurse into this value
				binary_output_write_value(L, -1, -2, ar);
				lua_pop(L, 1); // Pop value
				lua_pop(L, 1); // Pop type
			}
			lua_pop(L, 1); // Continue iterating by popping table value
			field_i++;
		}
	} else {
		throw Exception(ss_()+"Unknown type \""+outfield_type+"\""
					  "; known types are "+known_types);
	}
}

// cereal_binary_input(data: string, types: table) -> table of values
static int l_cereal_binary_input(lua_State *L)
{
	size_t data_len = 0;
	const char *data_c = lua_tolstring(L, 1, &data_len);
	ss_ data(data_c, data_len);

	int type_L = 2;

	std::istringstream is(data, std::ios::binary);
	cereal::PortableBinaryInputArchive ar(is);

	binary_input_read_value(L, type_L, ar);

	return 1;
}

// cereal_binary_output(values: table, types: table) -> data
static int l_cereal_binary_output(lua_State *L)
{
	int value_L = 1;

	int type_L = 2;

	std::ostringstream os(std::ios::binary);
	{
		cereal::PortableBinaryOutputArchive ar(os);

		binary_output_write_value(L, value_L, type_L, ar);
	}
	ss_ data = os.str();
	lua_pushlstring(L, data.c_str(), data.size());
	return 1;
}

void init_cereal(lua_State *L)
{
#define DEF_BUILDAT_FUNC(name){ \
		lua_pushcfunction(L, l_##name); \
		lua_setglobal(L, "__buildat_" #name); \
}
	DEF_BUILDAT_FUNC(cereal_binary_input)
	DEF_BUILDAT_FUNC(cereal_binary_output)
}

} // namespace lua_bindingss

// vim: set noet ts=4 sw=4:
<commit_msg>lua_bindings/cereal: support int16_t<commit_after>// http://www.apache.org/licenses/LICENSE-2.0
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#include "lua_bindings/util.h"
#include "core/log.h"
#include <cereal/archives/portable_binary.hpp>
#include <cereal/types/string.hpp>
#define MODULE "lua_bindings"

namespace lua_bindings {

/* Type format:
{"object",
    {"peer", "int32_t"},
    {"players", {"unordered_map",
        "int32_t",
        {"object",
            {"peer", "int32_t"},
            {"x", "int32_t"},
            {"y", "int32_t"},
        },
    }},
    {"playfield", {"object",
        {"w", "int32_t"},
        {"h", "int32_t"},
        {"tiles", {"array", "int32_t"}},
    }},
}) */

static constexpr auto known_types =
		"byte, int16_t, int32_t, double, array, unordered_map, object";

// Places result value on top of stack
static void binary_input_read_value(lua_State *L, int type_L,
		cereal::PortableBinaryInputArchive &ar)
{
	if(type_L < 0) type_L = lua_gettop(L) + type_L + 1;

	// Read value definition
	bool has_table = false;
	ss_ outfield_type;
	if(lua_istable(L, type_L)){
		has_table = true;
		lua_rawgeti(L, type_L, 1);
		outfield_type = lua_tocppstring(L, -1);
		lua_pop(L, 1);
	} else if(lua_isstring(L, type_L)){
		outfield_type = lua_tocppstring(L, type_L);
	} else {
		throw Exception("Value definition table or string expected");
	}

	log_t(MODULE, "binary_input_read_value(): type=%s", cs(outfield_type));

	if(outfield_type == "byte"){
		uchar value;
		ar(value);
		log_t(MODULE, "byte value=%i", (int)value);
		lua_pushinteger(L, value);
		// value is left on stack
	} else if(outfield_type == "int16_t"){
		int16_t value;
		ar(value);
		log_t(MODULE, "int16_t value=%i", value);
		lua_pushinteger(L, value);
		// value is left on stack
	} else if(outfield_type == "int32_t"){
		int32_t value;
		ar(value);
		log_t(MODULE, "int32_t value=%i", value);
		lua_pushinteger(L, value);
		// value is left on stack
	} else if(outfield_type == "double"){
		double value;
		ar(value);
		log_t(MODULE, "double value=%f", value);
		lua_pushnumber(L, value);
		// value is left on stack
	} else if(outfield_type == "string"){
		ss_ value;
		ar(value);
		log_t(MODULE, "string value=%s", cs(value));
		lua_pushlstring(L, value.c_str(), value.size());
		// value is left on stack
	} else if(outfield_type == "array"){
		if(!has_table)
			throw Exception("array requires parameter table");
		lua_newtable(L);
		int value_result_table_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 2);
		int array_type_L = lua_gettop(L);
		// Loop through array items
		uint64_t num_entries;
		ar(num_entries);
		for(uint64_t i = 0; i < num_entries; i++){
			log_t(MODULE, "array[%s]", cs(i));
			binary_input_read_value(L, array_type_L, ar);
			lua_rawseti(L, value_result_table_L, i + 1);
		}
		lua_pop(L, 1); // array_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "unordered_map"){
		if(!has_table)
			throw Exception("unordered_map requires parameter table");
		lua_newtable(L);
		int value_result_table_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 2);
		int map_key_type_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 3);
		int map_value_type_L = lua_gettop(L);
		// Loop through map entries
		uint64_t num_entries;
		ar(num_entries);
		for(uint64_t i = 0; i < num_entries; i++){
			log_t(MODULE, "unordered_map[%s]", cs(i));
			binary_input_read_value(L, map_key_type_L, ar);
			binary_input_read_value(L, map_value_type_L, ar);
			lua_rawset(L, value_result_table_L);
		}
		lua_pop(L, 1); // map_value_type_L
		lua_pop(L, 1); // map_key_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "object"){
		if(!has_table)
			throw Exception("object requires parameter table");
		lua_newtable(L);
		int value_result_table_L = lua_gettop(L);
		// Loop through object fields
		size_t field_i = 0;
		lua_pushnil(L);
		while(lua_next(L, type_L) != 0){
			if(field_i != 0){
				log_t(MODULE, "object field %zu", field_i);
				int field_def_L = lua_gettop(L);
				lua_rawgeti(L, field_def_L, 1); // name
				lua_rawgeti(L, field_def_L, 2); // type
				log_t(MODULE, " = object[\"%s\"]", lua_tostring(L, -2));
				binary_input_read_value(L, -1, ar); // Uses type, pushes value
				lua_remove(L, -2); // Remove type
				lua_rawset(L, value_result_table_L); // Set t[#-2] = #-1
			}
			lua_pop(L, 1); // Continue iterating by popping table value
			field_i++;
		}
		// value_result_table_L is left on stack
	} else {
		throw Exception(ss_()+"Unknown type \""+outfield_type+"\""
					  "; known types are "+known_types);
	}
}

static void binary_output_write_value(lua_State *L, int value_L, int type_L,
		cereal::PortableBinaryOutputArchive &ar)
{
	if(value_L < 0) value_L = lua_gettop(L) + value_L + 1;
	if(type_L < 0) type_L = lua_gettop(L) + type_L + 1;

	// Read value definition
	bool has_table = false;
	ss_ outfield_type;
	if(lua_istable(L, type_L)){
		has_table = true;
		lua_rawgeti(L, type_L, 1);
		outfield_type = lua_tocppstring(L, -1);
		lua_pop(L, 1);
	} else if(lua_isstring(L, type_L)){
		outfield_type = lua_tocppstring(L, type_L);
	} else {
		throw Exception("Value definition table or string expected");
	}

	log_t(MODULE, "binary_output_write_value(): type=%s", cs(outfield_type));

	if(outfield_type == "byte"){
		uchar value = lua_tointeger(L, value_L);
		log_t(MODULE, "byte value=%i", (int)value);
		ar(value);
	} else if(outfield_type == "int16_t"){
		int16_t value = lua_tointeger(L, value_L);
		log_t(MODULE, "int16_t value=%i", value);
		ar(value);
	} else if(outfield_type == "int32_t"){
		int32_t value = lua_tointeger(L, value_L);
		log_t(MODULE, "int32_t value=%i", value);
		ar(value);
	} else if(outfield_type == "double"){
		double value = lua_tonumber(L, value_L);
		log_t(MODULE, "double value=%f", value);
		ar(value);
	} else if(outfield_type == "string"){
		ss_ value = lua_tocppstring(L, value_L);
		log_t(MODULE, "string value=%s", cs(value));
		ar(value);
	} else if(outfield_type == "array"){
		if(!has_table)
			throw Exception("array requires parameter table");
		lua_rawgeti(L, type_L, 2);
		int array_type_L = lua_gettop(L);
		// Loop through array items
		uint64_t num_entries = 0;
		lua_pushnil(L);
		while(lua_next(L, value_L) != 0){
			lua_pop(L, 1); // Continue iterating by popping table value
			num_entries++;
		}
		ar(num_entries);
		lua_pushnil(L);
		int i = 1;
		while(lua_next(L, value_L) != 0){
			log_t(MODULE, "array[%i]", i);
			binary_output_write_value(L, -1, array_type_L, ar);
			lua_pop(L, 1); // Continue iterating by popping table value
			i++;
		}
		lua_pop(L, 1); // array_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "unordered_map"){
		if(!has_table)
			throw Exception("unordered_map requires parameter table");
		lua_rawgeti(L, type_L, 2);
		int map_key_type_L = lua_gettop(L);
		lua_rawgeti(L, type_L, 3);
		int map_value_type_L = lua_gettop(L);
		// Loop through map entries
		uint64_t num_entries = 0;
		lua_pushnil(L);
		while(lua_next(L, value_L) != 0){
			lua_pop(L, 1); // Continue iterating by popping table value
			num_entries++;
		}
		ar(num_entries);
		lua_pushnil(L);
		while(lua_next(L, value_L) != 0){
			int key_L = lua_gettop(L) - 1;
			int value_L = lua_gettop(L);
			log_t(MODULE, "unordered_map[%s]", lua_tostring(L, key_L));
			binary_output_write_value(L, key_L, map_key_type_L, ar);
			binary_output_write_value(L, value_L, map_value_type_L, ar);
			lua_pop(L, 1); // Continue iterating by popping table value
		}
		lua_pop(L, 1); // map_value_type_L
		lua_pop(L, 1); // map_key_type_L
		// value_result_table_L is left on stack
	} else if(outfield_type == "object"){
		if(!has_table)
			throw Exception("object requires parameter table");
		// Loop through object fields
		size_t field_i = 0;
		lua_pushnil(L);
		while(lua_next(L, type_L) != 0){
			if(field_i != 0){
				log_t(MODULE, "object field %zu", field_i);
				int field_def_L = lua_gettop(L);
				lua_rawgeti(L, field_def_L, 2); // type
				lua_rawgeti(L, field_def_L, 1); // name
				log_t(MODULE, " = object[\"%s\"]", lua_tostring(L, -1));
				// Get value_L[name]; name is replaced by value
				lua_rawget(L, value_L);
				// Recurse into this value
				binary_output_write_value(L, -1, -2, ar);
				lua_pop(L, 1); // Pop value
				lua_pop(L, 1); // Pop type
			}
			lua_pop(L, 1); // Continue iterating by popping table value
			field_i++;
		}
	} else {
		throw Exception(ss_()+"Unknown type \""+outfield_type+"\""
					  "; known types are "+known_types);
	}
}

// cereal_binary_input(data: string, types: table) -> table of values
static int l_cereal_binary_input(lua_State *L)
{
	size_t data_len = 0;
	const char *data_c = lua_tolstring(L, 1, &data_len);
	ss_ data(data_c, data_len);

	int type_L = 2;

	std::istringstream is(data, std::ios::binary);
	cereal::PortableBinaryInputArchive ar(is);

	binary_input_read_value(L, type_L, ar);

	return 1;
}

// cereal_binary_output(values: table, types: table) -> data
static int l_cereal_binary_output(lua_State *L)
{
	int value_L = 1;

	int type_L = 2;

	std::ostringstream os(std::ios::binary);
	{
		cereal::PortableBinaryOutputArchive ar(os);

		binary_output_write_value(L, value_L, type_L, ar);
	}
	ss_ data = os.str();
	lua_pushlstring(L, data.c_str(), data.size());
	return 1;
}

void init_cereal(lua_State *L)
{
#define DEF_BUILDAT_FUNC(name){ \
		lua_pushcfunction(L, l_##name); \
		lua_setglobal(L, "__buildat_" #name); \
}
	DEF_BUILDAT_FUNC(cereal_binary_input)
	DEF_BUILDAT_FUNC(cereal_binary_output)
}

} // namespace lua_bindingss

// vim: set noet ts=4 sw=4:
<|endoftext|>