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

/**
 *    @file
 *      This file implements a test for  CHIP Interaction Model Event logging
 *
 */

#include <app/ClusterInfo.h>
#include <app/EventLoggingDelegate.h>
#include <app/EventLoggingTypes.h>
#include <app/EventManagement.h>
#include <app/InteractionModelEngine.h>
#include <app/tests/AppTestContext.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/CHIPTLVDebug.hpp>
#include <lib/core/CHIPTLVUtilities.hpp>
#include <lib/support/EnforceFormat.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/UnitTestRegistration.h>
#include <lib/support/logging/Constants.h>
#include <messaging/ExchangeContext.h>
#include <messaging/Flags.h>
#include <platform/CHIPDeviceLayer.h>
#include <system/TLVPacketBufferBackingStore.h>

#include <nlunit-test.h>

namespace {

static uint8_t gDebugEventBuffer[2048];
static uint8_t gInfoEventBuffer[2048];
static uint8_t gCritEventBuffer[2048];
static chip::app::CircularEventBuffer gCircularEventBuffer[3];

class TestContext : public chip::Test::AppContext
{
public:
    static int InitializeAsync(void * context)
    {
        if (AppContext::InitializeAsync(context) != SUCCESS)
            return FAILURE;

        auto * ctx = static_cast<TestContext *>(context);

        chip::app::LogStorageResources logStorageResources[] = {
            { &gDebugEventBuffer[0], sizeof(gDebugEventBuffer), chip::app::PriorityLevel::Debug },
            { &gInfoEventBuffer[0], sizeof(gInfoEventBuffer), chip::app::PriorityLevel::Info },
            { &gCritEventBuffer[0], sizeof(gCritEventBuffer), chip::app::PriorityLevel::Critical },
        };

        chip::app::EventManagement::CreateEventManagement(&ctx->GetExchangeManager(),
                                                          sizeof(logStorageResources) / sizeof(logStorageResources[0]),
                                                          gCircularEventBuffer, logStorageResources, nullptr, 0, nullptr);

        return SUCCESS;
    }

    static int Finalize(void * context)
    {
        chip::app::EventManagement::DestroyEventManagement();

        if (AppContext::Finalize(context) != SUCCESS)
            return FAILURE;

        return SUCCESS;
    }
};

class TestEventGenerator : public chip::app::EventLoggingDelegate
{
public:
    CHIP_ERROR WriteEvent(chip::TLV::TLVWriter & aWriter)
    {
        chip::TLV::TLVType dataContainerType;
        ReturnErrorOnFailure(aWriter.StartContainer(chip::TLV::ContextTag(chip::to_underlying(chip::app::EventDataIB::Tag::kData)),
                                                    chip::TLV::kTLVType_Structure, dataContainerType));
        ReturnErrorOnFailure(aWriter.Put(chip::TLV::ContextTag(1), 1));
        ReturnErrorOnFailure(aWriter.Put(chip::TLV::ContextTag(2), 2));
        return aWriter.EndContainer(dataContainerType);
    }
};

static void CheckLogEventOverFlow(nlTestSuite * apSuite, void * apContext)
{
    CHIP_ERROR err           = CHIP_NO_ERROR;
    chip::EventNumber oldEid = 0;
    chip::EventNumber eid    = 0;
    chip::app::EventOptions options;
    TestEventGenerator testEventGenerator;

    chip::EndpointId testEndpointId = 1;
    chip::ClusterId testClusterId   = 0x00000006;
    chip::EventId testEvent         = 1;
    options.mPath                   = { testEndpointId, testClusterId, testEvent };
    options.mPriority               = chip::app::PriorityLevel::Debug;

    chip::app::EventManagement & logMgmt = chip::app::EventManagement::GetInstance();
    int alternate                        = 0;
    for (int i = 0; i < 500; i++)
    {
        switch (alternate)
        {
        case 0:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 1:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 2:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 3:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 4:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 5:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 6:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 7:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 8:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 9:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        }
        alternate = i % 10;

        err = logMgmt.LogEvent(&testEventGenerator, options, eid);
        NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
        if (eid > 0)
        {
            NL_TEST_ASSERT(apSuite, eid == oldEid + 1);
            oldEid = eid;
        }
    }
}

const nlTest sTests[] = { NL_TEST_DEF("CheckLogEventOverFlow", CheckLogEventOverFlow), NL_TEST_SENTINEL() };

// clang-format off
nlTestSuite sSuite =
{
    "TestEventOverflow",
    &sTests[0],
    TestContext::InitializeAsync,
    TestContext::Finalize
};
// clang-format on

} // namespace

int TestEventOverflow()
{
    TestContext gContext;
    nlTestRunner(&sSuite, &gContext);
    return (nlTestRunnerStats(&sSuite));
}

CHIP_REGISTER_TEST_SUITE(TestEventOverflow)
<commit_msg>Fix TLV put cast (#15747)<commit_after>/*
 *
 *    Copyright (c) 2022 Project CHIP Authors
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *      This file implements a test for  CHIP Interaction Model Event logging
 *
 */

#include <app/ClusterInfo.h>
#include <app/EventLoggingDelegate.h>
#include <app/EventLoggingTypes.h>
#include <app/EventManagement.h>
#include <app/InteractionModelEngine.h>
#include <app/tests/AppTestContext.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/CHIPTLVDebug.hpp>
#include <lib/core/CHIPTLVUtilities.hpp>
#include <lib/support/EnforceFormat.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/UnitTestRegistration.h>
#include <lib/support/logging/Constants.h>
#include <messaging/ExchangeContext.h>
#include <messaging/Flags.h>
#include <platform/CHIPDeviceLayer.h>
#include <system/TLVPacketBufferBackingStore.h>

#include <nlunit-test.h>

namespace {

static uint8_t gDebugEventBuffer[2048];
static uint8_t gInfoEventBuffer[2048];
static uint8_t gCritEventBuffer[2048];
static chip::app::CircularEventBuffer gCircularEventBuffer[3];

class TestContext : public chip::Test::AppContext
{
public:
    static int InitializeAsync(void * context)
    {
        if (AppContext::InitializeAsync(context) != SUCCESS)
            return FAILURE;

        auto * ctx = static_cast<TestContext *>(context);

        chip::app::LogStorageResources logStorageResources[] = {
            { &gDebugEventBuffer[0], sizeof(gDebugEventBuffer), chip::app::PriorityLevel::Debug },
            { &gInfoEventBuffer[0], sizeof(gInfoEventBuffer), chip::app::PriorityLevel::Info },
            { &gCritEventBuffer[0], sizeof(gCritEventBuffer), chip::app::PriorityLevel::Critical },
        };

        chip::app::EventManagement::CreateEventManagement(&ctx->GetExchangeManager(),
                                                          sizeof(logStorageResources) / sizeof(logStorageResources[0]),
                                                          gCircularEventBuffer, logStorageResources, nullptr, 0, nullptr);

        return SUCCESS;
    }

    static int Finalize(void * context)
    {
        chip::app::EventManagement::DestroyEventManagement();

        if (AppContext::Finalize(context) != SUCCESS)
            return FAILURE;

        return SUCCESS;
    }
};

class TestEventGenerator : public chip::app::EventLoggingDelegate
{
public:
    CHIP_ERROR WriteEvent(chip::TLV::TLVWriter & aWriter)
    {
        chip::TLV::TLVType dataContainerType;
        ReturnErrorOnFailure(aWriter.StartContainer(chip::TLV::ContextTag(chip::to_underlying(chip::app::EventDataIB::Tag::kData)),
                                                    chip::TLV::kTLVType_Structure, dataContainerType));
        ReturnErrorOnFailure(aWriter.Put(chip::TLV::ContextTag(1), static_cast<uint32_t>(1)));
        ReturnErrorOnFailure(aWriter.Put(chip::TLV::ContextTag(2), static_cast<uint32_t>(2)));
        return aWriter.EndContainer(dataContainerType);
    }
};

static void CheckLogEventOverFlow(nlTestSuite * apSuite, void * apContext)
{
    CHIP_ERROR err           = CHIP_NO_ERROR;
    chip::EventNumber oldEid = 0;
    chip::EventNumber eid    = 0;
    chip::app::EventOptions options;
    TestEventGenerator testEventGenerator;

    chip::EndpointId testEndpointId = 1;
    chip::ClusterId testClusterId   = 0x00000006;
    chip::EventId testEvent         = 1;
    options.mPath                   = { testEndpointId, testClusterId, testEvent };
    options.mPriority               = chip::app::PriorityLevel::Debug;

    chip::app::EventManagement & logMgmt = chip::app::EventManagement::GetInstance();
    int alternate                        = 0;
    for (int i = 0; i < 500; i++)
    {
        switch (alternate)
        {
        case 0:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 1:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 2:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 3:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 4:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 5:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 6:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 7:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        case 8:
            options.mPriority = chip::app::PriorityLevel::Critical;
            break;
        case 9:
            options.mPriority = chip::app::PriorityLevel::Debug;
            break;
        }
        alternate = i % 10;

        err = logMgmt.LogEvent(&testEventGenerator, options, eid);
        NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
        if (eid > 0)
        {
            NL_TEST_ASSERT(apSuite, eid == oldEid + 1);
            oldEid = eid;
        }
    }
}

const nlTest sTests[] = { NL_TEST_DEF("CheckLogEventOverFlow", CheckLogEventOverFlow), NL_TEST_SENTINEL() };

// clang-format off
nlTestSuite sSuite =
{
    "TestEventOverflow",
    &sTests[0],
    TestContext::InitializeAsync,
    TestContext::Finalize
};
// clang-format on

} // namespace

int TestEventOverflow()
{
    TestContext gContext;
    nlTestRunner(&sSuite, &gContext);
    return (nlTestRunnerStats(&sSuite));
}

CHIP_REGISTER_TEST_SUITE(TestEventOverflow)
<|endoftext|>


<commit_before>#ifndef ___INANITY_INANITY_GRAPHICS_HPP___
#define ___INANITY_INANITY_GRAPHICS_HPP___

/* Файл, содержащий ссылки на конкретную реализацию графической подсистемы.
 */

#ifdef ___INANITY_WINDOWS___

// В Windows используется подсистема DX, использующая DirectX 11

// Заголовочные файлы.

#include "dx/System.hpp"

// Определения.

namespace Graphics
{
	typedef DX::System System;
	typedef DX::Texture Texture;
	typedef DX::ConstantBuffer ConstantBuffer;
	typedef DX::Geometry Geometry;
	typedef DX::GeometryFormat GeometryFormat;
};

#endif

#endif
<commit_msg>inanity graphics header fixes<commit_after>#ifndef ___INANITY_INANITY_GRAPHICS_HPP___
#define ___INANITY_INANITY_GRAPHICS_HPP___

/* Файл, содержащий ссылки на конкретную реализацию графической подсистемы.
 */

// В Windows используется подсистема DX, использующая DirectX 11
#ifdef ___INANITY_WINDOWS
#define ___INANITY_GRAPHICS_DX
#else
#error There is no implemented graphics system for current platform.
#endif

#include "graphics/graphics.hpp"
#include "graphics/System.hpp"
#include "graphics/Texture.hpp"
#include "graphics/Window.hpp"
#include "graphics/PixelFormat.hpp"
#include "graphics/Geometry.hpp"
#include "graphics/GeometryFormat.hpp"
#include "graphics/GeometrySemantic.hpp"

#endif
<|endoftext|>


<commit_before>#include "SerialPort.h"

SerialPort::SerialPort(char *portName)
{
    this->connected = false;

    this->handler = CreateFileA(static_cast<LPCSTR>(portName),
                                GENERIC_READ | GENERIC_WRITE,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (this->handler == INVALID_HANDLE_VALUE){
        if (GetLastError() == ERROR_FILE_NOT_FOUND){
            printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
        }
    else
        {
            printf("ERROR!!!");
        }
    }
    else {
        DCB dcbSerialParameters = {0};

        if (!GetCommState(this->handler, &dcbSerialParameters)) {
            printf("failed to get current serial parameters");
        }
        else {
            dcbSerialParameters.BaudRate = CBR_9600;
            dcbSerialParameters.ByteSize = 8;
            dcbSerialParameters.StopBits = ONESTOPBIT;
            dcbSerialParameters.Parity = NOPARITY;
            dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

            if (!SetCommState(handler, &dcbSerialParameters))
            {
                printf("ALERT: could not set Serial port parameters\n");
            }
            else {
                this->connected = true;
                PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
                Sleep(ARDUINO_WAIT_TIME);
            }
        }
    }
}

SerialPort::~SerialPort()
{
    if (this->connected){
        this->connected = false;
        CloseHandle(this->handler);
    }
}

int SerialPort::readSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesRead;
    unsigned int toRead;

    ClearCommError(this->handler, &this->errors, &this->status);

    if (this->status.cbInQue > 0){
        if (this->status.cbInQue > buf_size){
            toRead = buf_size;
        }
        else toRead = this->status.cbInQue;
    }

    if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;

    return 0;
}

bool SerialPort::writeSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesSend;

    if (!WriteFile(this->handler, (void*) buffer, buf_size, &bytesSend, 0)){
        ClearCommError(this->handler, &this->errors, &this->status);
        return false;
    }
    else return true;
}

bool SerialPort::isConnected()
{
    return this->connected;
}
<commit_msg>Update SerialPort.cpp<commit_after>/*
* Author: Manash Kumar Mandal
* Modified Library introduced in Arduino Playground which does not work
* This works perfectly
* LICENSE: MIT
*/

#include "SerialPort.h"

SerialPort::SerialPort(char *portName)
{
    this->connected = false;

    this->handler = CreateFileA(static_cast<LPCSTR>(portName),
                                GENERIC_READ | GENERIC_WRITE,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (this->handler == INVALID_HANDLE_VALUE){
        if (GetLastError() == ERROR_FILE_NOT_FOUND){
            printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
        }
    else
        {
            printf("ERROR!!!");
        }
    }
    else {
        DCB dcbSerialParameters = {0};

        if (!GetCommState(this->handler, &dcbSerialParameters)) {
            printf("failed to get current serial parameters");
        }
        else {
            dcbSerialParameters.BaudRate = CBR_9600;
            dcbSerialParameters.ByteSize = 8;
            dcbSerialParameters.StopBits = ONESTOPBIT;
            dcbSerialParameters.Parity = NOPARITY;
            dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

            if (!SetCommState(handler, &dcbSerialParameters))
            {
                printf("ALERT: could not set Serial port parameters\n");
            }
            else {
                this->connected = true;
                PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
                Sleep(ARDUINO_WAIT_TIME);
            }
        }
    }
}

SerialPort::~SerialPort()
{
    if (this->connected){
        this->connected = false;
        CloseHandle(this->handler);
    }
}

int SerialPort::readSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesRead;
    unsigned int toRead;

    ClearCommError(this->handler, &this->errors, &this->status);

    if (this->status.cbInQue > 0){
        if (this->status.cbInQue > buf_size){
            toRead = buf_size;
        }
        else toRead = this->status.cbInQue;
    }

    if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;

    return 0;
}

bool SerialPort::writeSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesSend;

    if (!WriteFile(this->handler, (void*) buffer, buf_size, &bytesSend, 0)){
        ClearCommError(this->handler, &this->errors, &this->status);
        return false;
    }
    else return true;
}

bool SerialPort::isConnected()
{
    return this->connected;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2001,2002 Steven M. Cherry. All rights reserved.
 *
 * This file is a part of slib - a c++ utility library
 *
 * The slib project, including all files needed to compile
 * it, is free software; you can redistribute it and/or use it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  See file COPYING for details.
 */

#include "SmtpClient.h"
#include "Date.h"
#include "EnEx.h"
#include "Log.h"
#include "memptr.h"
#include "dptr.h"
#include "AnException.h"
#include "XmlHelpers.h"
#include "TmpFile.h"
using namespace SLib;

static bool SmtpClient_cURL_Initialized = false;

/** This one is used by the object (non-static) version of the methods below.
  */
size_t SmtpClient_ReadMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
	SmtpClient* client = (SmtpClient*)userp;

	//printf("SmtpClient_ReadMemoryCallback(contents, %ld, %ld, userp) SendIndex = %ld\n", size, nmemb, client->SendIndex);

	// If they don't want us to ready any memory, bail out quickly.
	if( (size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
		//printf("SmtpClient_ReadMemoryCallback - size/nmemb is zero\n");
		return 0;
	}

	// If we've sent all of our memory already, bail out quickly
	if(client->SendIndex >= client->SendLines.size()){
		//printf("SmtpClient_ReadMemoryCallback - nothing more to send.\n");
		return 0; // nothing more to send
	}

	size_t len = client->SendLines[ client->SendIndex ].size();
	memcpy(contents, client->SendLines[ client->SendIndex ](), len );
	client->SendIndex ++;
	return len;

	/*
	// How much are they asking for?
	size_t realsize = size * nmemb;

	if( realsize > (client->SendBuffer.size() - client->SendIndex) ){
		// They are asking for more data than we have.  Send it all:
		memcpy( contents,  // Where to write the memory
			client->SendBuffer() + client->SendIndex,  // Where to read the memory
			client->SendBuffer.size() - client->SendIndex // How much to write
		);

		printf("Smtp_ReadMemoryCallback - wrote %ld bytes to buffer\n",
			client->SendBuffer.size() - client->SendIndex
		);

		client->SendIndex = client->SendBuffer.size();
		return (client->SendBuffer.size() - client->SendIndex); // how much did we write
	} else {
		// They are asking for less than what we have.  Send only what they
		// ask for
		memcpy( contents, // where to write the memory
			client->SendBuffer() + client->SendIndex, // Where to read the memory
			realsize // How much to write
		);

		printf("Smtp_ReadMemoryCallback - wrote %ld bytes to buffer\n",
			realsize
		);

		client->SendIndex += realsize; // Keep track of where we are in the buffer
		return realsize; // Hom much did we write
	}
	*/
}

/** This one is used to report progress of downloads
  */
int SmtpClient_ProgressCallback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
	SmtpClient* client = (SmtpClient*)clientp;
	return client->Progress(dltotal, dlnow, ultotal, ulnow);
}

SmtpClient::SmtpClient()
{
	EnEx ee(FL, "SmtpClient::SmtpClient()");

	if(SmtpClient_cURL_Initialized == false){
		curl_global_init(CURL_GLOBAL_ALL);
		SmtpClient_cURL_Initialized = true;
	}
	m_curl_handle = curl_easy_init();
}

SmtpClient::~SmtpClient()
{
	EnEx ee(FL, "SmtpClient::~SmtpClient()");

	curl_easy_cleanup( m_curl_handle );
}

int SmtpClient::Progress(double dltotal, double dlnow, double ultotal, double ulnow)
{
	// If we return a non-zero value, it will abort the transfer.
	return 0;
}

void SmtpClient::Send(EMail& message, const twine& smtpServer, const twine& user, const twine& pass,
	int port, bool useSsl)
{
	EnEx ee(FL, "SmtpClient::Send(EMail& message, const twine& smtpServer, const twine& user, const twine& pass, int port, bool useSsl)");

	TmpFile verboseLog; // Create a temp file to hold the verbose log - will be cleaned up when this method exits
	SendLines.clear();
	SendIndex = 0;
	FormatMessage( message );

	//printf("Here's what we're sending:\n%s\n", SendLines() );

	CURLcode res = CURLE_OK;
	char errbuf[ CURL_ERROR_SIZE ];
	memset(errbuf, 0, CURL_ERROR_SIZE);
	struct curl_slist *recipients = NULL;

	// Set the user name and password
	curl_easy_setopt( m_curl_handle, CURLOPT_USERNAME, user() );
	curl_easy_setopt( m_curl_handle, CURLOPT_PASSWORD, pass() );

	// Set up the URL for the mail server.  Default port is 25, 587 is commonly used for
	// secure mail submission.
	twine url; url.format("smtp://%s:%d", smtpServer(), port);
	curl_easy_setopt( m_curl_handle, CURLOPT_URL, url() );

	// If they have asked for a secure connection, ensure that it's setup.
	if(useSsl){
		curl_easy_setopt( m_curl_handle, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL );
		// Ignore SSL cert issues
		curl_easy_setopt( m_curl_handle, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt( m_curl_handle, CURLOPT_SSL_VERIFYHOST, false);
	}

	curl_easy_setopt( m_curl_handle, CURLOPT_MAIL_FROM, message.From()() );

	for(size_t i = 0; i < message.TOList().size(); i++){
		recipients = curl_slist_append(recipients, message.TOList()[ i ]() );
	}
	for(size_t i = 0; i < message.CCList().size(); i++){
		recipients = curl_slist_append(recipients, message.CCList()[ i ]() );
	}
	curl_easy_setopt( m_curl_handle, CURLOPT_MAIL_RCPT, recipients );

	curl_easy_setopt( m_curl_handle, CURLOPT_READFUNCTION, SmtpClient_ReadMemoryCallback );
	curl_easy_setopt( m_curl_handle, CURLOPT_READDATA, this );
	curl_easy_setopt( m_curl_handle, CURLOPT_UPLOAD, 1L );

	// For progress information
	curl_easy_setopt( m_curl_handle, CURLOPT_NOPROGRESS, 0L);
	curl_easy_setopt( m_curl_handle, CURLOPT_PROGRESSFUNCTION, SmtpClient_ProgressCallback );
	curl_easy_setopt( m_curl_handle, CURLOPT_PROGRESSDATA, this );

	curl_easy_setopt( m_curl_handle, CURLOPT_VERBOSE, 1L );
	curl_easy_setopt( m_curl_handle, CURLOPT_ERRORBUFFER, errbuf );
	curl_easy_setopt( m_curl_handle, CURLOPT_STDERR, (FILE*)verboseLog );

	// Add in proxy information if given
	if(!m_proxy.empty()){
		DEBUG(FL, "Using proxy of (%s)", m_proxy() );
		curl_easy_setopt( m_curl_handle, CURLOPT_PROXY, m_proxy() );
	}

	PostOptions();

	res = curl_easy_perform( m_curl_handle );
	verboseLog.flush();
	twine errmsg;
	if(res != CURLE_OK){
		WARN(FL, "Sending SMTP Message failed: %s: %s\n%s",
			curl_easy_strerror(res), errbuf, verboseLog.readContentsAsTwine()()
		);
		errmsg.format("Sending SMTP Message failed: %s: %s", curl_easy_strerror(res), errbuf );
		//printf("%s", errmsg() );
	}

	// Always clean up and free lists
	curl_slist_free_all(recipients);
	curl_easy_reset( m_curl_handle );

	PostFree();

	// Now check to see if we need to throw an exception
	if(res != CURLE_OK){
		throw AnException(0, FL, errmsg() );
	}

}

void SmtpClient::SetProxy(const twine& proxyUrl )
{
	EnEx ee(FL, "SmtpClient::SetProxy(const twine& proxyUrl)");

	m_proxy = proxyUrl;
}

void SmtpClient::PostOptions()
{
	// Our implementation does nothing with this method.  Child classes can override this as necessary.
}

void SmtpClient::PostFree()
{
	// Our implementation does nothing with this method.  Child classes can override this as necessary.
}

void SmtpClient::FormatMessage(EMail& message )
{
	EnEx ee(FL, "SmtpClient::FormatMessage(EMail& message)" );

	//printf("here0\n");
	SendLines.clear();
	SendIndex = 0;

	twine tmp;

	if(message.ReplyTo().size() == 0){
		message.ReplyTo(message.From());
	}
	twine boundary;  boundary.format("SLibSmtpClient=%s", message.CreateDate().GetValue("%Y%m%d%H%M%S")() );

	tmp.format("From: %s\r\n", message.From()() ); SendLines.push_back( tmp );
	tmp.format("Subject: %s\r\n", message.Subject()() ); SendLines.push_back( tmp );
	tmp.format("Date: %s\r\n", message.CreateDate().EDate()() ); SendLines.push_back( tmp );
	tmp.format("Reply-To: %s\r\n", message.ReplyTo()() ); SendLines.push_back( tmp );

	// Add in any custom headers the user has specified
	for(size_t i = 0; i < message.HeaderList().size(); i++){
		tmp.format("%s: %s\r\n", message.HeaderList()[i].first(), message.HeaderList()[i].second() );
		SendLines.push_back( tmp );
	}

	tmp.format("MIME-Version: 1.0\r\n" ); SendLines.push_back(tmp);
	tmp.format("Content-Type: multipart/mixed; boundary=\"%s\"\r\n", boundary() ); SendLines.push_back(tmp);
	tmp = "To:";
	for(size_t i = 0; i < message.TOList().size()-1; i++){
		tmp += " " + message.TOList()[i] + ",";
	}
	tmp += " " + message.TOList()[ message.TOList().size() - 1] + "\r\n";
	SendLines.push_back( tmp );

	if(message.CCList().size() > 0){
		tmp = "Cc:";
		for(size_t i = 0; i < message.CCList().size()-1; i++){
			tmp += " " + message.CCList()[i] + ",";
		}
		tmp += " " + message.CCList()[ message.CCList().size() - 1] + "\r\n";
		SendLines.push_back( tmp );
	}

	SendLines.push_back( "\r\n" ); // Empty line to divide headers from body

	SendLines.push_back( "--" + boundary + "\r\n" );
	SendLines.push_back( "Content-Type: text/plain; charset=utf-8\r\n");
	SendLines.push_back( "\r\n" ); // Empty line to divide headers from body

	vector<twine> bodyLines = message.Body().split("\n");
	for(size_t i = 0; i < bodyLines.size(); i++){
		SendLines.push_back( bodyLines[i] + "\r\n" );
	}

	vector< EMailAttachment >& attachments = message.AttachmentList();
	for(size_t i = 0; i < attachments.size(); i++){
		SendLines.push_back( "\r\n--" + boundary + "\r\n" );
		tmp.format( "Content-Type: %s; charset=utf-8\r\n", attachments[i].mimeType() );
		SendLines.push_back( tmp );
		tmp.format( "Content-Disposition: attachment; filename=\"%s\"\r\n", attachments[i].fileName());
		SendLines.push_back( tmp );
		SendLines.push_back( "Content-Transfer-Encoding: base64\r\n");
		SendLines.push_back( "\r\n" ); // Empty line to divide headers from body

		// Make a copy of the data for base64 encoding:
		MemBuf dataCopy( *attachments[i].data );

		// base64 encode the attachment copy
		dataCopy.encode64();

		twine b64; b64.set( dataCopy.data(), dataCopy.size() );
		vector<twine> attachLines = b64.split("\n");
		for(size_t j = 0; j < attachLines.size(); j++){
			SendLines.push_back( attachLines[j] + "\r\n" );
		}
		SendLines.push_back( "\r\n" ); // Terminate the data
	}

	SendLines.push_back( "\r\n" ); // Empty line to signal end of section
	SendLines.push_back( "--" + boundary + "--\r\n" ); // Last boundary has -- before and after boundary text

	/*
	for(size_t i = 0; i < SendLines.size(); i++){
		printf("%s", SendLines[i]() );
	}
	*/

}
<commit_msg>Omit empty e-mail addresses.<commit_after>/*
 * Copyright (c) 2001,2002 Steven M. Cherry. All rights reserved.
 *
 * This file is a part of slib - a c++ utility library
 *
 * The slib project, including all files needed to compile
 * it, is free software; you can redistribute it and/or use it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  See file COPYING for details.
 */

#include "SmtpClient.h"
#include "Date.h"
#include "EnEx.h"
#include "Log.h"
#include "memptr.h"
#include "dptr.h"
#include "AnException.h"
#include "XmlHelpers.h"
#include "TmpFile.h"
using namespace SLib;

static bool SmtpClient_cURL_Initialized = false;

/** This one is used by the object (non-static) version of the methods below.
  */
size_t SmtpClient_ReadMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
	SmtpClient* client = (SmtpClient*)userp;

	//printf("SmtpClient_ReadMemoryCallback(contents, %ld, %ld, userp) SendIndex = %ld\n", size, nmemb, client->SendIndex);

	// If they don't want us to ready any memory, bail out quickly.
	if( (size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
		//printf("SmtpClient_ReadMemoryCallback - size/nmemb is zero\n");
		return 0;
	}

	// If we've sent all of our memory already, bail out quickly
	if(client->SendIndex >= client->SendLines.size()){
		//printf("SmtpClient_ReadMemoryCallback - nothing more to send.\n");
		return 0; // nothing more to send
	}

	size_t len = client->SendLines[ client->SendIndex ].size();
	memcpy(contents, client->SendLines[ client->SendIndex ](), len );
	client->SendIndex ++;
	return len;

	/*
	// How much are they asking for?
	size_t realsize = size * nmemb;

	if( realsize > (client->SendBuffer.size() - client->SendIndex) ){
		// They are asking for more data than we have.  Send it all:
		memcpy( contents,  // Where to write the memory
			client->SendBuffer() + client->SendIndex,  // Where to read the memory
			client->SendBuffer.size() - client->SendIndex // How much to write
		);

		printf("Smtp_ReadMemoryCallback - wrote %ld bytes to buffer\n",
			client->SendBuffer.size() - client->SendIndex
		);

		client->SendIndex = client->SendBuffer.size();
		return (client->SendBuffer.size() - client->SendIndex); // how much did we write
	} else {
		// They are asking for less than what we have.  Send only what they
		// ask for
		memcpy( contents, // where to write the memory
			client->SendBuffer() + client->SendIndex, // Where to read the memory
			realsize // How much to write
		);

		printf("Smtp_ReadMemoryCallback - wrote %ld bytes to buffer\n",
			realsize
		);

		client->SendIndex += realsize; // Keep track of where we are in the buffer
		return realsize; // Hom much did we write
	}
	*/
}

/** This one is used to report progress of downloads
  */
int SmtpClient_ProgressCallback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
	SmtpClient* client = (SmtpClient*)clientp;
	return client->Progress(dltotal, dlnow, ultotal, ulnow);
}

SmtpClient::SmtpClient()
{
	EnEx ee(FL, "SmtpClient::SmtpClient()");

	if(SmtpClient_cURL_Initialized == false){
		curl_global_init(CURL_GLOBAL_ALL);
		SmtpClient_cURL_Initialized = true;
	}
	m_curl_handle = curl_easy_init();
}

SmtpClient::~SmtpClient()
{
	EnEx ee(FL, "SmtpClient::~SmtpClient()");

	curl_easy_cleanup( m_curl_handle );
}

int SmtpClient::Progress(double dltotal, double dlnow, double ultotal, double ulnow)
{
	// If we return a non-zero value, it will abort the transfer.
	return 0;
}

void SmtpClient::Send(EMail& message, const twine& smtpServer, const twine& user, const twine& pass,
	int port, bool useSsl)
{
	EnEx ee(FL, "SmtpClient::Send(EMail& message, const twine& smtpServer, const twine& user, const twine& pass, int port, bool useSsl)");

	TmpFile verboseLog; // Create a temp file to hold the verbose log - will be cleaned up when this method exits
	SendLines.clear();
	SendIndex = 0;
	FormatMessage( message );

	//printf("Here's what we're sending:\n%s\n", SendLines() );

	CURLcode res = CURLE_OK;
	char errbuf[ CURL_ERROR_SIZE ];
	memset(errbuf, 0, CURL_ERROR_SIZE);
	struct curl_slist *recipients = NULL;

	// Set the user name and password
	curl_easy_setopt( m_curl_handle, CURLOPT_USERNAME, user() );
	curl_easy_setopt( m_curl_handle, CURLOPT_PASSWORD, pass() );

	// Set up the URL for the mail server.  Default port is 25, 587 is commonly used for
	// secure mail submission.
	twine url; url.format("smtp://%s:%d", smtpServer(), port);
	curl_easy_setopt( m_curl_handle, CURLOPT_URL, url() );

	// If they have asked for a secure connection, ensure that it's setup.
	if(useSsl){
		curl_easy_setopt( m_curl_handle, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL );
		// Ignore SSL cert issues
		curl_easy_setopt( m_curl_handle, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt( m_curl_handle, CURLOPT_SSL_VERIFYHOST, false);
	}

	curl_easy_setopt( m_curl_handle, CURLOPT_MAIL_FROM, message.From()() );

	for(auto& to : message.TOList()){
		if(to.empty() == false){
			recipients = curl_slist_append(recipients, to() );
		}
	}
	for(auto& cc : message.CCList()){
		if(cc.empty() == false){
			recipients = curl_slist_append(recipients, cc() );
		}
	}
	curl_easy_setopt( m_curl_handle, CURLOPT_MAIL_RCPT, recipients );

	curl_easy_setopt( m_curl_handle, CURLOPT_READFUNCTION, SmtpClient_ReadMemoryCallback );
	curl_easy_setopt( m_curl_handle, CURLOPT_READDATA, this );
	curl_easy_setopt( m_curl_handle, CURLOPT_UPLOAD, 1L );

	// For progress information
	curl_easy_setopt( m_curl_handle, CURLOPT_NOPROGRESS, 0L);
	curl_easy_setopt( m_curl_handle, CURLOPT_PROGRESSFUNCTION, SmtpClient_ProgressCallback );
	curl_easy_setopt( m_curl_handle, CURLOPT_PROGRESSDATA, this );

	curl_easy_setopt( m_curl_handle, CURLOPT_VERBOSE, 1L );
	curl_easy_setopt( m_curl_handle, CURLOPT_ERRORBUFFER, errbuf );
	curl_easy_setopt( m_curl_handle, CURLOPT_STDERR, (FILE*)verboseLog );

	// Add in proxy information if given
	if(!m_proxy.empty()){
		DEBUG(FL, "Using proxy of (%s)", m_proxy() );
		curl_easy_setopt( m_curl_handle, CURLOPT_PROXY, m_proxy() );
	}

	PostOptions();

	res = curl_easy_perform( m_curl_handle );
	verboseLog.flush();
	twine errmsg;
	if(res != CURLE_OK){
		WARN(FL, "Sending SMTP Message failed: %s: %s\n%s",
			curl_easy_strerror(res), errbuf, verboseLog.readContentsAsTwine()()
		);
		errmsg.format("Sending SMTP Message failed: %s: %s", curl_easy_strerror(res), errbuf );
		//printf("%s", errmsg() );
	}

	// Always clean up and free lists
	curl_slist_free_all(recipients);
	curl_easy_reset( m_curl_handle );

	PostFree();

	// Now check to see if we need to throw an exception
	if(res != CURLE_OK){
		throw AnException(0, FL, errmsg() );
	}

}

void SmtpClient::SetProxy(const twine& proxyUrl )
{
	EnEx ee(FL, "SmtpClient::SetProxy(const twine& proxyUrl)");

	m_proxy = proxyUrl;
}

void SmtpClient::PostOptions()
{
	// Our implementation does nothing with this method.  Child classes can override this as necessary.
}

void SmtpClient::PostFree()
{
	// Our implementation does nothing with this method.  Child classes can override this as necessary.
}

void SmtpClient::FormatMessage(EMail& message )
{
	EnEx ee(FL, "SmtpClient::FormatMessage(EMail& message)" );

	//printf("here0\n");
	SendLines.clear();
	SendIndex = 0;

	twine tmp;

	if(message.ReplyTo().size() == 0){
		message.ReplyTo(message.From());
	}
	twine boundary;  boundary.format("SLibSmtpClient=%s", message.CreateDate().GetValue("%Y%m%d%H%M%S")() );

	tmp.format("From: %s\r\n", message.From()() ); SendLines.push_back( tmp );
	tmp.format("Subject: %s\r\n", message.Subject()() ); SendLines.push_back( tmp );
	tmp.format("Date: %s\r\n", message.CreateDate().EDate()() ); SendLines.push_back( tmp );
	tmp.format("Reply-To: %s\r\n", message.ReplyTo()() ); SendLines.push_back( tmp );

	// Add in any custom headers the user has specified
	for(size_t i = 0; i < message.HeaderList().size(); i++){
		tmp.format("%s: %s\r\n", message.HeaderList()[i].first(), message.HeaderList()[i].second() );
		SendLines.push_back( tmp );
	}

	tmp.format("MIME-Version: 1.0\r\n" ); SendLines.push_back(tmp);
	tmp.format("Content-Type: multipart/mixed; boundary=\"%s\"\r\n", boundary() ); SendLines.push_back(tmp);
	tmp = "To:";
	for(size_t i = 0; i < message.TOList().size()-1; i++){
		tmp += " " + message.TOList()[i] + ",";
	}
	tmp += " " + message.TOList()[ message.TOList().size() - 1] + "\r\n";
	SendLines.push_back( tmp );

	if(message.CCList().size() > 0){
		tmp = "Cc:";
		for(size_t i = 0; i < message.CCList().size()-1; i++){
			tmp += " " + message.CCList()[i] + ",";
		}
		tmp += " " + message.CCList()[ message.CCList().size() - 1] + "\r\n";
		SendLines.push_back( tmp );
	}

	SendLines.push_back( "\r\n" ); // Empty line to divide headers from body

	SendLines.push_back( "--" + boundary + "\r\n" );
	SendLines.push_back( "Content-Type: text/plain; charset=utf-8\r\n");
	SendLines.push_back( "\r\n" ); // Empty line to divide headers from body

	vector<twine> bodyLines = message.Body().split("\n");
	for(size_t i = 0; i < bodyLines.size(); i++){
		SendLines.push_back( bodyLines[i] + "\r\n" );
	}

	vector< EMailAttachment >& attachments = message.AttachmentList();
	for(size_t i = 0; i < attachments.size(); i++){
		SendLines.push_back( "\r\n--" + boundary + "\r\n" );
		tmp.format( "Content-Type: %s; charset=utf-8\r\n", attachments[i].mimeType() );
		SendLines.push_back( tmp );
		tmp.format( "Content-Disposition: attachment; filename=\"%s\"\r\n", attachments[i].fileName());
		SendLines.push_back( tmp );
		SendLines.push_back( "Content-Transfer-Encoding: base64\r\n");
		SendLines.push_back( "\r\n" ); // Empty line to divide headers from body

		// Make a copy of the data for base64 encoding:
		MemBuf dataCopy( *attachments[i].data );

		// base64 encode the attachment copy
		dataCopy.encode64();

		twine b64; b64.set( dataCopy.data(), dataCopy.size() );
		vector<twine> attachLines = b64.split("\n");
		for(size_t j = 0; j < attachLines.size(); j++){
			SendLines.push_back( attachLines[j] + "\r\n" );
		}
		SendLines.push_back( "\r\n" ); // Terminate the data
	}

	SendLines.push_back( "\r\n" ); // Empty line to signal end of section
	SendLines.push_back( "--" + boundary + "--\r\n" ); // Last boundary has -- before and after boundary text

	/*
	for(size_t i = 0; i < SendLines.size(); i++){
		printf("%s", SendLines[i]() );
	}
	*/

}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
//  The environment should contain the following bindings:
//  * name - skeleton tree
//  * name - annotation                 <--- TODO ?
//
///////////////////////////////////////////////////////////////////////////////
#ifndef rpl_environment_hpp
#define rpl_environment_hpp

#include <map>
#include <memory>

template <typename T>
using snode_ptr = std::shared_ptr<T>;

template <typename K, typename V>
struct environment {
    snode_ptr<V> get(K& key);
    void put(K& id, V* value);
private:
    std::map<K, snode_ptr<V>> env;
};

template <typename K, typename V>
snode_ptr<V> environment<K,V>::get(K& key) {
    return env.at(key);
}

template <typename K, typename V>
void environment<K,V>::put(K& id, V* value) {
    snode_ptr<V> sptr(value);
    env[id] = sptr;
}

#endif
<commit_msg>multimap internal representation<commit_after>///////////////////////////////////////////////////////////////////////////////
//
//  The environment contain the following type of bindings:
//  * K -> { V }
//
//  Environment implemented internally as a multimap
//
///////////////////////////////////////////////////////////////////////////////
#ifndef rpl_environment_hpp
#define rpl_environment_hpp

#include <map>
#include <utility>
#include <memory>

// useful typedefs
template <typename T>
using ptr = std::shared_ptr<T>;

template <typename K, typename V>
using iterator = typename std::multimap<K, V>::iterator;

template <typename K, typename V>
struct environment
{
    typedef ::iterator<K, ptr<V>> it;

    /// returns a range containing all the elements with given key
    std::pair<it, it> range( const K& key );

    // get an element for the given key
    ptr<V> get(const K& key);

    // substitutes old <key,value> pair with the new one
    void put(const K& key, V* value);

    // add the <key,value> pair in the environment
    void add(const K& key, V* value);

    // modify content at pos
    void modify(it pos, V* value);

    // clear content for given key
    void clear( const K& key );

private:
    std::multimap<K, ptr<V>> env;
};

template <typename K, typename V>
std::pair<::iterator<K, ptr<V>>, ::iterator<K, ptr<V>>> environment<K,V>::range( const K& key )
{
    return env.equal_range( key );
}

template <typename K, typename V>
ptr<V> environment<K,V>::get( const K& key )
{
    auto it = env.find(key);
    return (it != env.end()) ? it->second : nullptr;
}

template <typename K, typename V>
void environment<K,V>::put(const K& key, V* value)
{
    env.erase( key );
    env.insert({ key, ptr<V>(value) });
}

template <typename K, typename V>
void environment<K,V>::add(const K& key, V* value)
{
    env.insert({ key, ptr<V>(value) });
}

template <typename K, typename V>
void environment<K,V>::modify(::iterator<K, ptr<V>> pos, V* value)
{
    if (value != nullptr)
        pos->second = ptr<V>(value);
}

template <typename K, typename V>
void environment<K,V>::clear(const K& k)
{
    env.erase(k);
}
#endif
<|endoftext|>


<commit_before><commit_msg>update version NO<commit_after><|endoftext|>


<commit_before>//Copyright (c) 2019 Ultimaker B.V.

#include "Statistics.h"

#include <sstream>
#include <fstream>

#include "utils/logoutput.h"

namespace arachne
{

void Statistics::analyse(Polygons& input, std::vector<std::vector<std::vector<ExtrusionJunction>>>& polygons_per_index, std::vector<std::vector<std::vector<ExtrusionJunction>>>& polylines_per_index, VoronoiQuadrangulation* vq)
{
    this->input = &input;
    this->vq = vq;
    this->polygons_per_index = &polygons_per_index;
    this->polylines_per_index = &polylines_per_index;

    generateAllSegments(polygons_per_index, polylines_per_index);

    for (coord_t segment_idx = 0; segment_idx < all_segments.size(); segment_idx++)
    {
        Segment s = all_segments[segment_idx];
        Polygons covered = s.s.toPolygons(false);
        area_covered.add(covered);
        Polygons extruded = s.toPolygons();
        overlaps.add(extruded);
    }

    area_covered = area_covered.execute(ClipperLib::pftNonZero);

    overfills = overlaps;
    for (PolygonRef poly : area_covered)
    {
        PolygonRef new_poly = overfills.newPoly();
        for (coord_t point_idx = poly.size() - 1; point_idx >= 0; --point_idx)
        {
            new_poly.add(poly[point_idx]);
        }
    }
    double_overfills = overfills;
    for (PolygonRef poly : area_covered)
    {
        PolygonRef new_poly = double_overfills.newPoly();
        for (coord_t point_idx = poly.size() - 1; point_idx >= 0; --point_idx)
        {
            new_poly.add(poly[point_idx]);
        }
    }
    overfills = overfills.execute(ClipperLib::pftPositive);
    overfills = overfills.intersection(area_covered);
    overfills = overfills.offset(-5);
    overfills = overfills.offset(10);
    overfills = overfills.offset(-5);

    double_overfills = double_overfills.execute(ClipperLib::pftPositive);
    double_overfills = double_overfills.offset(-5);
    double_overfills = double_overfills.offset(10);
    double_overfills = double_overfills.offset(-5);

    overfill_area = INT2MM2(overfills.area());
    double_overfill_area = INT2MM2(double_overfills.area());
    double total_overfill_area = overfill_area + double_overfill_area;
//     logAlways("Total overfill area: %f mm²\n", total_overfill_area);

    underfills = input.difference(area_covered);
    underfills = underfills.offset(5);
    underfills = underfills.offset(-10);
    underfills = underfills.offset(5);

    total_underfill_area = INT2MM2(underfills.area());
//     logAlways("Total underfill area: %f mm²\n", total_underfill_area);
//     std::vector<PolygonsPart> underfill_areas = underfills.splitIntoParts();
//     logAlways("Average area: %f mm² over %d parts\n", total_underfill_area / underfill_areas.size(), underfill_areas.size());

    total_target_area = INT2MM2(input.area());
    total_target_area_length = INT2MM(input.polygonLength());
//     logAlways("Total target area: %f mm²\n", total_target_area);

    // initialize paths
    for (Segment& segment : all_segments)
    {
        PolygonRef poly = paths.newPoly();
        poly.emplace_back(segment.s.from.p);
        poly.emplace_back(segment.s.to.p);
    }

}

void Statistics::saveResultsCSV()
{

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_segments.csv";
        std::ofstream csv(ss.str(), std::ofstream::out | std::ofstream::trunc);
        csv << "from_x,from_y,from_width,to_x,to_y,to_width,filename_base,output_prefix\n";
        for (const Segment& segment : all_segments)
            csv << segment.s.from.p.X << "," << segment.s.from.p.Y << "," << segment.s.from.w << ","
                << segment.s.to.p.X << "," << segment.s.to.p.Y << "," << segment.s.to.w << ","
                << test_type << "," << output_prefix << '\n';
        csv.close();
    }
    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_results.csv";
        std::ofstream csv(ss.str(), std::ofstream::out | std::ofstream::trunc);
        csv << "processing_time,overfill_area,double_overfill_area,total_underfill_area,total_target_area,total_target_area_length,test_type,output_prefix\n";
        csv << processing_time << "," << overfill_area << "," << double_overfill_area << "," << total_underfill_area << ","
            << total_target_area << "," << total_target_area_length << ","
            << test_type << "," << output_prefix << '\n';
        csv.close();
    }
}

void Statistics::generateAllSegments(std::vector<std::vector<std::vector<ExtrusionJunction>>>& polygons_per_index, std::vector<std::vector<std::vector<ExtrusionJunction>>>& polylines_per_index)
{
    for (std::vector<std::vector<ExtrusionJunction>>& polygons : polygons_per_index)
    {
        for (std::vector<ExtrusionJunction>& polygon : polygons)
        {
            ExtrusionJunction last = polygon.back();
            for (coord_t junction_idx = 0; junction_idx < polygon.size(); junction_idx++)
            {
                ExtrusionJunction& junction = polygon[junction_idx];
                ExtrusionSegment segment(last, junction, false);
                all_segments.emplace_back(segment, false);
                last = junction;
            }
        }
    }
    for (std::vector<std::vector<ExtrusionJunction>>& polylines : polylines_per_index)
    {
        for (std::vector<ExtrusionJunction>& polyline : polylines)
        {
            ExtrusionJunction last = polyline.front();
            for (coord_t junction_idx = 0; junction_idx < polyline.size(); junction_idx++)
            {
                ExtrusionJunction& junction = polyline[junction_idx];
                ExtrusionSegment segment(last, junction, false);
                all_segments.emplace_back(segment, junction_idx == polyline.size() - 1);
                last = junction;
            }
        }
    }
}

void Statistics::visualize()
{
    AABB aabb(*input);

    if (vq)
    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_after.svg";
        SVG svg(ss.str(), aabb);
        vq->debugOutput(svg, false, false, true);
        svg.writePolygons(paths, SVG::Color::BLACK, 2);

        if (false)
        for (auto polys : *polylines_per_index)
        {
            for (auto poly : polys)
            {
                Point prev = poly.front().p;
                for (ExtrusionJunction& j : poly)
                {
                    svg.writeLine(prev, j.p, SVG::Color::RED, 2);
                    prev = j.p;
                }
            }
        }
        for (auto polylines : *polylines_per_index)
        {
            for (std::vector<ExtrusionJunction>& polyline : polylines)
            {
                svg.writePoint(polyline.front().p, false, 2, SVG::Color::GREEN);
                svg.writePoint(polyline.back().p, false, 2, SVG::Color::BLUE);
            }
        }
    }

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_toolpaths.svg";
        SVG svg(ss.str(), aabb);
        svg.writeAreas(*input, SVG::Color::GRAY, SVG::Color::NONE, 2);
        bool alternate = true;
        for (PolygonRef poly : overlaps)
        {
            svg.writeAreas(poly, alternate? SVG::Color::BLUE : SVG::Color::MAGENTA, SVG::Color::NONE);
            alternate = !alternate;
        }
        svg.writePolygons(paths, SVG::Color::BLACK, 2);
    }

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_widths.svg";
        SVG svg(ss.str(), aabb);
//         svg.writeAreas(*input, SVG::Color::GRAY, SVG::Color::NONE, 2);

        coord_t max_dev = 200;
        coord_t min_w = 30;

        // add legend
        auto to_string = [](float v)
        {
            std::ostringstream ss;
            ss << v;
            return ss.str();
        };
        std::vector<Segment> all_segments_plus = all_segments;
        AABB aabb(*input);
        ExtrusionJunction legend_btm(Point(aabb.max.X + 400 + max_dev, aabb.max.Y), 400 - max_dev, 0);
        ExtrusionJunction legend_top(Point(aabb.max.X + 400 + max_dev, aabb.min.Y), 400 + max_dev, 0);
        ExtrusionJunction legend_mid((legend_top.p + legend_btm.p) / 2, (legend_top.w + legend_btm.w) / 2, 0);
        legend_btm.p += (legend_mid.p - legend_btm.p) / 4;
        legend_top.p += (legend_mid.p - legend_top.p) / 4;
        ExtrusionSegment legend_segment(legend_btm, legend_top, true);
        svg.writeAreas(legend_segment.toPolygons(false), SVG::ColorObject(200,200,200), SVG::Color::NONE); // real outline
        all_segments_plus.emplace_back(legend_segment, true); // colored
        Point legend_text_offset(400, 0);
        svg.writeText(legend_top.p + legend_text_offset, to_string(INT2MM(legend_top.w)));
        svg.writeText(legend_btm.p + legend_text_offset, to_string(INT2MM(legend_btm.w)));
        svg.writeText(legend_mid.p + legend_text_offset, to_string(INT2MM(legend_mid.w)));
        svg.writeLine(legend_top.p, legend_top.p + legend_text_offset);
        svg.writeLine(legend_btm.p, legend_btm.p + legend_text_offset);
        svg.writeLine(legend_mid.p, legend_mid.p + legend_text_offset);

        Point3 green(0,255,0);
        Point3 red(255,0,0);
        Point3 blue(0,0,255);
        for (const Segment& ss : all_segments_plus)
        {
            for (Segment s : discretize(ss, MM2INT(0.1)))
            {
                coord_t avg_w = (s.s.from.w + s.s.to.w) / 2;
                Point3 clr;
                float color_ratio = std::min(1.0, std::abs(avg_w - 400.0) / max_dev);
                color_ratio = color_ratio * .5 + .5 * sqrt(color_ratio);
                if (avg_w > 400)
                {
                    clr = red * color_ratio + green * (1.0 - color_ratio );
                }
                else
                {
                    clr = blue * color_ratio + green * (1.0 - color_ratio );
                }
                coord_t clr_max = std::max(clr.x, std::max(clr.y, clr.z));
                clr = clr * 255 / clr_max;

                clr.y = clr.y * (255 - 92 * clr.dot(green) / green.vSize() / 255) / 255;
                s.s.from.w = std::max(min_w, min_w + (s.s.from.w - (400 - max_dev)) * 5 / 4);
                s.s.to.w = std::max(min_w, min_w + (s.s.to.w - (400 - max_dev)) * 5 / 4);
                Polygons covered = s.toPolygons();
                svg.writeAreas(covered, SVG::ColorObject(clr.x, clr.y, clr.z), SVG::Color::NONE);
            }
        }
//         svg.writePolygons(paths, SVG::Color::BLACK, 1);
    }

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_accuracy.svg";
        SVG svg(ss.str(), aabb);
        svg.writeAreas(*input, SVG::Color::GRAY, SVG::Color::NONE, 3);
        svg.writeAreas(overfills, SVG::Color::RED, SVG::Color::NONE);
        svg.writeAreas(double_overfills, SVG::Color::ORANGE, SVG::Color::NONE);
        svg.writeAreas(underfills, SVG::Color::BLUE, SVG::Color::NONE);
        svg.writePolygons(paths, SVG::Color::BLACK, 1);
    }
}

std::vector<Statistics::Segment> Statistics::discretize(const Segment& segment, coord_t step_size)
{
    ExtrusionSegment extrusion_segment = segment.s;
    Point a = extrusion_segment.from.p;
    Point b = extrusion_segment.to.p;
    Point ab = b - a;
    coord_t ab_length = vSize(ab);
    coord_t step_count = std::max(static_cast<coord_t>(1), (ab_length + step_size / 2) / step_size);
    std::vector<Segment> discretized;
    ExtrusionJunction from = extrusion_segment.from;
    for (coord_t step = 0; step < step_count; step++)
    {
        ExtrusionJunction mid(a + ab * (step + 1) / step_count, extrusion_segment.from.w + (extrusion_segment.to.w - extrusion_segment.from.w) * (step + 1) / step_count, extrusion_segment.from.perimeter_index);
        discretized.emplace_back(ExtrusionSegment(from, mid, segment.s.is_odd), false);
        from = mid;
    }
    discretized.back().is_full = segment.is_full;
    return discretized;
}

} // namespace arachne
<commit_msg>fix visualization of overfills outside of input outline shape<commit_after>//Copyright (c) 2019 Ultimaker B.V.

#include "Statistics.h"

#include <sstream>
#include <fstream>

#include "utils/logoutput.h"

namespace arachne
{

void Statistics::analyse(Polygons& input, std::vector<std::vector<std::vector<ExtrusionJunction>>>& polygons_per_index, std::vector<std::vector<std::vector<ExtrusionJunction>>>& polylines_per_index, VoronoiQuadrangulation* vq)
{
    this->input = &input;
    this->vq = vq;
    this->polygons_per_index = &polygons_per_index;
    this->polylines_per_index = &polylines_per_index;

    generateAllSegments(polygons_per_index, polylines_per_index);

    for (coord_t segment_idx = 0; segment_idx < all_segments.size(); segment_idx++)
    {
        Segment s = all_segments[segment_idx];
        Polygons covered = s.s.toPolygons(false);
        area_covered.add(covered);
        Polygons extruded = s.toPolygons();
        overlaps.add(extruded);
    }

    area_covered = area_covered.execute(ClipperLib::pftNonZero);

    overfills = overlaps;
    for (PolygonRef poly : area_covered)
    {
        PolygonRef new_poly = overfills.newPoly();
        for (coord_t point_idx = poly.size() - 1; point_idx >= 0; --point_idx)
        {
            new_poly.add(poly[point_idx]);
        }
    }
    overfills.add(area_covered.difference(input));

    double_overfills = overfills;
    for (PolygonRef poly : area_covered)
    {
        PolygonRef new_poly = double_overfills.newPoly();
        for (coord_t point_idx = poly.size() - 1; point_idx >= 0; --point_idx)
        {
            new_poly.add(poly[point_idx]);
        }
    }
    overfills = overfills.execute(ClipperLib::pftPositive);
    overfills = overfills.intersection(area_covered);
    overfills = overfills.offset(-5);
    overfills = overfills.offset(10);
    overfills = overfills.offset(-5);

    double_overfills = double_overfills.execute(ClipperLib::pftPositive);
    double_overfills = double_overfills.offset(-5);
    double_overfills = double_overfills.offset(10);
    double_overfills = double_overfills.offset(-5);

    overfill_area = INT2MM2(overfills.area());
    double_overfill_area = INT2MM2(double_overfills.area());
    double total_overfill_area = overfill_area + double_overfill_area;
//     logAlways("Total overfill area: %f mm²\n", total_overfill_area);

    underfills = input.difference(area_covered);
    underfills = underfills.offset(5);
    underfills = underfills.offset(-10);
    underfills = underfills.offset(5);

    total_underfill_area = INT2MM2(underfills.area());
//     logAlways("Total underfill area: %f mm²\n", total_underfill_area);
//     std::vector<PolygonsPart> underfill_areas = underfills.splitIntoParts();
//     logAlways("Average area: %f mm² over %d parts\n", total_underfill_area / underfill_areas.size(), underfill_areas.size());

    total_target_area = INT2MM2(input.area());
    total_target_area_length = INT2MM(input.polygonLength());
//     logAlways("Total target area: %f mm²\n", total_target_area);

    // initialize paths
    for (Segment& segment : all_segments)
    {
        PolygonRef poly = paths.newPoly();
        poly.emplace_back(segment.s.from.p);
        poly.emplace_back(segment.s.to.p);
    }

}

void Statistics::saveResultsCSV()
{

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_segments.csv";
        std::ofstream csv(ss.str(), std::ofstream::out | std::ofstream::trunc);
        csv << "from_x,from_y,from_width,to_x,to_y,to_width,filename_base,output_prefix\n";
        for (const Segment& segment : all_segments)
            csv << segment.s.from.p.X << "," << segment.s.from.p.Y << "," << segment.s.from.w << ","
                << segment.s.to.p.X << "," << segment.s.to.p.Y << "," << segment.s.to.w << ","
                << test_type << "," << output_prefix << '\n';
        csv.close();
    }
    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_results.csv";
        std::ofstream csv(ss.str(), std::ofstream::out | std::ofstream::trunc);
        csv << "processing_time,overfill_area,double_overfill_area,total_underfill_area,total_target_area,total_target_area_length,test_type,output_prefix\n";
        csv << processing_time << "," << overfill_area << "," << double_overfill_area << "," << total_underfill_area << ","
            << total_target_area << "," << total_target_area_length << ","
            << test_type << "," << output_prefix << '\n';
        csv.close();
    }
}

void Statistics::generateAllSegments(std::vector<std::vector<std::vector<ExtrusionJunction>>>& polygons_per_index, std::vector<std::vector<std::vector<ExtrusionJunction>>>& polylines_per_index)
{
    for (std::vector<std::vector<ExtrusionJunction>>& polygons : polygons_per_index)
    {
        for (std::vector<ExtrusionJunction>& polygon : polygons)
        {
            ExtrusionJunction last = polygon.back();
            for (coord_t junction_idx = 0; junction_idx < polygon.size(); junction_idx++)
            {
                ExtrusionJunction& junction = polygon[junction_idx];
                ExtrusionSegment segment(last, junction, false);
                all_segments.emplace_back(segment, false);
                last = junction;
            }
        }
    }
    for (std::vector<std::vector<ExtrusionJunction>>& polylines : polylines_per_index)
    {
        for (std::vector<ExtrusionJunction>& polyline : polylines)
        {
            ExtrusionJunction last = polyline.front();
            for (coord_t junction_idx = 0; junction_idx < polyline.size(); junction_idx++)
            {
                ExtrusionJunction& junction = polyline[junction_idx];
                ExtrusionSegment segment(last, junction, false);
                all_segments.emplace_back(segment, junction_idx == polyline.size() - 1);
                last = junction;
            }
        }
    }
}

void Statistics::visualize()
{
    AABB aabb(*input);

    if (vq)
    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_after.svg";
        SVG svg(ss.str(), aabb);
        vq->debugOutput(svg, false, false, true);
        svg.writePolygons(paths, SVG::Color::BLACK, 2);

        if (false)
        for (auto polys : *polylines_per_index)
        {
            for (auto poly : polys)
            {
                Point prev = poly.front().p;
                for (ExtrusionJunction& j : poly)
                {
                    svg.writeLine(prev, j.p, SVG::Color::RED, 2);
                    prev = j.p;
                }
            }
        }
        for (auto polylines : *polylines_per_index)
        {
            for (std::vector<ExtrusionJunction>& polyline : polylines)
            {
                svg.writePoint(polyline.front().p, false, 2, SVG::Color::GREEN);
                svg.writePoint(polyline.back().p, false, 2, SVG::Color::BLUE);
            }
        }
    }

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_toolpaths.svg";
        SVG svg(ss.str(), aabb);
        svg.writeAreas(*input, SVG::Color::GRAY, SVG::Color::NONE, 2);
        bool alternate = true;
        for (PolygonRef poly : overlaps)
        {
            svg.writeAreas(poly, alternate? SVG::Color::BLUE : SVG::Color::MAGENTA, SVG::Color::NONE);
            alternate = !alternate;
        }
        svg.writePolygons(paths, SVG::Color::BLACK, 2);
    }

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_widths.svg";
        SVG svg(ss.str(), aabb);
//         svg.writeAreas(*input, SVG::Color::GRAY, SVG::Color::NONE, 2);

        coord_t max_dev = 200;
        coord_t min_w = 30;

        // add legend
        auto to_string = [](float v)
        {
            std::ostringstream ss;
            ss << v;
            return ss.str();
        };
        std::vector<Segment> all_segments_plus = all_segments;
        AABB aabb(*input);
        ExtrusionJunction legend_btm(Point(aabb.max.X + 400 + max_dev, aabb.max.Y), 400 - max_dev, 0);
        ExtrusionJunction legend_top(Point(aabb.max.X + 400 + max_dev, aabb.min.Y), 400 + max_dev, 0);
        ExtrusionJunction legend_mid((legend_top.p + legend_btm.p) / 2, (legend_top.w + legend_btm.w) / 2, 0);
        legend_btm.p += (legend_mid.p - legend_btm.p) / 4;
        legend_top.p += (legend_mid.p - legend_top.p) / 4;
        ExtrusionSegment legend_segment(legend_btm, legend_top, true);
        svg.writeAreas(legend_segment.toPolygons(false), SVG::ColorObject(200,200,200), SVG::Color::NONE); // real outline
        all_segments_plus.emplace_back(legend_segment, true); // colored
        Point legend_text_offset(400, 0);
        svg.writeText(legend_top.p + legend_text_offset, to_string(INT2MM(legend_top.w)));
        svg.writeText(legend_btm.p + legend_text_offset, to_string(INT2MM(legend_btm.w)));
        svg.writeText(legend_mid.p + legend_text_offset, to_string(INT2MM(legend_mid.w)));
        svg.writeLine(legend_top.p, legend_top.p + legend_text_offset);
        svg.writeLine(legend_btm.p, legend_btm.p + legend_text_offset);
        svg.writeLine(legend_mid.p, legend_mid.p + legend_text_offset);

        Point3 green(0,255,0);
        Point3 red(255,0,0);
        Point3 blue(0,0,255);
        for (const Segment& ss : all_segments_plus)
        {
            for (Segment s : discretize(ss, MM2INT(0.1)))
            {
                coord_t avg_w = (s.s.from.w + s.s.to.w) / 2;
                Point3 clr;
                float color_ratio = std::min(1.0, std::abs(avg_w - 400.0) / max_dev);
                color_ratio = color_ratio * .5 + .5 * sqrt(color_ratio);
                if (avg_w > 400)
                {
                    clr = red * color_ratio + green * (1.0 - color_ratio );
                }
                else
                {
                    clr = blue * color_ratio + green * (1.0 - color_ratio );
                }
                coord_t clr_max = std::max(clr.x, std::max(clr.y, clr.z));
                clr = clr * 255 / clr_max;

                clr.y = clr.y * (255 - 92 * clr.dot(green) / green.vSize() / 255) / 255;
                s.s.from.w = std::max(min_w, min_w + (s.s.from.w - (400 - max_dev)) * 5 / 4);
                s.s.to.w = std::max(min_w, min_w + (s.s.to.w - (400 - max_dev)) * 5 / 4);
                Polygons covered = s.toPolygons();
                svg.writeAreas(covered, SVG::ColorObject(clr.x, clr.y, clr.z), SVG::Color::NONE);
            }
        }
//         svg.writePolygons(paths, SVG::Color::BLACK, 1);
    }

    {
        std::ostringstream ss;
        ss << "output/" << output_prefix << "_" << test_type << "_accuracy.svg";
        SVG svg(ss.str(), aabb);
        svg.writeAreas(*input, SVG::Color::GRAY, SVG::Color::NONE, 3);
        svg.writeAreas(overfills, SVG::Color::RED, SVG::Color::NONE);
        svg.writeAreas(double_overfills, SVG::Color::ORANGE, SVG::Color::NONE);
        svg.writeAreas(underfills, SVG::Color::BLUE, SVG::Color::NONE);
        svg.writePolygons(paths, SVG::Color::BLACK, 1);
    }
}

std::vector<Statistics::Segment> Statistics::discretize(const Segment& segment, coord_t step_size)
{
    ExtrusionSegment extrusion_segment = segment.s;
    Point a = extrusion_segment.from.p;
    Point b = extrusion_segment.to.p;
    Point ab = b - a;
    coord_t ab_length = vSize(ab);
    coord_t step_count = std::max(static_cast<coord_t>(1), (ab_length + step_size / 2) / step_size);
    std::vector<Segment> discretized;
    ExtrusionJunction from = extrusion_segment.from;
    for (coord_t step = 0; step < step_count; step++)
    {
        ExtrusionJunction mid(a + ab * (step + 1) / step_count, extrusion_segment.from.w + (extrusion_segment.to.w - extrusion_segment.from.w) * (step + 1) / step_count, extrusion_segment.from.perimeter_index);
        discretized.emplace_back(ExtrusionSegment(from, mid, segment.s.is_odd), false);
        from = mid;
    }
    discretized.back().is_full = segment.is_full;
    return discretized;
}

} // namespace arachne
<|endoftext|>


<commit_before>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <cerrno>

#include "z80.h"

namespace {

using z80::fast_u8;
using z80::fast_u16;
using z80::least_u8;
using z80::unused;

#if defined(__GNUC__) || defined(__clang__)
# define LIKE_PRINTF(format, args) \
      __attribute__((__format__(__printf__, format, args)))
#else
# define LIKE_PRINTF(format, args) /* nothing */
#endif

const char program_name[] = "benchmark";

[[noreturn]] LIKE_PRINTF(1, 0)
void verror(const char *format, va_list args) {
    std::fprintf(stderr, "%s: ", program_name);
    std::vfprintf(stderr, format, args);
    std::fprintf(stderr, "\n");
    exit(EXIT_FAILURE);
}

[[noreturn]] LIKE_PRINTF(1, 2)
void error(const char *format, ...) {
    va_list args;
    va_start(args, format);
    verror(format, args);
    va_end(args);
}

static constexpr fast_u16 quit_addr = 0x0000;
static constexpr fast_u16 bdos_addr = 0x0005;
static constexpr fast_u16 entry_addr = 0x0100;

// Handles CP/M BDOS calls to write text messages.
template<typename B>
class default_watcher : public B {
public:
    typedef B base;

    void write_char(fast_u8 c) {
        std::putchar(static_cast<char>(c));
    }

    void handle_c_write() {
        write_char(base::get_e());
    }

    void handle_c_writestr() {
        fast_u16 addr = base::get_de();
        for(;;) {
            fast_u8 c = self().on_read(addr);
            if(c == '$')
                break;

            write_char(c);
            addr = (addr + 1) % z80::address_space_size;
        }
    }

    void handle_bdos_call() {
        switch(base::get_c()) {
        case c_write:
            handle_c_write();
            break;
        case c_writestr:
            handle_c_writestr();
            break;
        }
    }

    void on_step() {
        if(base::get_pc() == bdos_addr)
            handle_bdos_call();

        base::on_step();
    }

    void on_report() {}

protected:
    using base::self;

private:
    static constexpr fast_u8 c_write = 0x02;
    static constexpr fast_u8 c_writestr = 0x09;
};

// Lets the emulator to perform at full speed.
template<typename B>
class empty_watcher : public B {
public:
    typedef B base;

    // The benchmark emulator provides no support for interrupts,
    // so no need to track the flag.
    // TODO: Remove that flag from the emulator's state at all.
    void on_set_is_int_disabled(bool f) { unused(f); }

    void on_report() {}

protected:
    using base::self;
};

// Tracks use of CPU state.
template<typename B>
class state_watcher : public B {
public:
    typedef B base;

    fast_u16 on_get_pc() { ++pc_reads; return base::on_get_pc(); }
    void on_set_pc(fast_u16 nn) { ++pc_writes; base::on_set_pc(nn); }

    fast_u16 on_get_sp() { ++sp_reads; return base::on_get_sp(); }
    void on_set_sp(fast_u16 nn) { ++sp_writes; base::on_set_sp(nn); }

    fast_u16 on_get_wz() { ++wz_reads; return base::on_get_wz(); }
    void on_set_wz(fast_u16 nn) { ++wz_writes; base::on_set_wz(nn); }

    fast_u16 on_get_bc() { ++bc_reads; return base::on_get_bc(); }
    void on_set_bc(fast_u16 nn) { ++bc_writes; base::on_set_bc(nn); }
    fast_u8 on_get_b() { ++b_reads; return base::on_get_b(); }
    void on_set_b(fast_u8 n) { ++b_writes; base::on_set_b(n); }
    fast_u8 on_get_c() { ++c_reads; return base::on_get_c(); }
    void on_set_c(fast_u8 n) { ++c_writes; base::on_set_c(n); }

    fast_u16 on_get_de() { ++de_reads; return base::on_get_de(); }
    void on_set_de(fast_u16 nn) { ++de_writes; base::on_set_de(nn); }
    fast_u8 on_get_d() { ++d_reads; return base::on_get_d(); }
    void on_set_d(fast_u8 n) { ++d_writes; base::on_set_d(n); }
    fast_u8 on_get_e() { ++e_reads; return base::on_get_e(); }
    void on_set_e(fast_u8 n) { ++e_writes; base::on_set_e(n); }

    fast_u16 on_get_hl() { ++hl_reads; return base::on_get_hl(); }
    void on_set_hl(fast_u16 nn) { ++hl_writes; base::on_set_hl(nn); }
    fast_u8 on_get_h() { ++h_reads; return base::on_get_h(); }
    void on_set_h(fast_u8 n) { ++h_writes; base::on_set_h(n); }
    fast_u8 on_get_l() { ++l_reads; return base::on_get_l(); }
    void on_set_l(fast_u8 n) { ++l_writes; base::on_set_l(n); }

    fast_u16 on_get_af() { ++af_reads; return base::on_get_af(); }
    void on_set_af(fast_u16 nn) { ++af_writes; base::on_set_af(nn); }
    fast_u8 on_get_a() { ++a_reads; return base::on_get_a(); }
    void on_set_a(fast_u8 n) { ++a_writes; base::on_set_a(n); }
    fast_u8 on_get_f() { ++f_reads; return base::on_get_f(); }
    void on_set_f(fast_u8 n) { ++f_writes; base::on_set_f(n); }

    bool on_is_int_disabled() {
        ++is_int_disabled_reads;
        return base::on_is_int_disabled(); }
    void on_set_is_int_disabled(bool f) {
        ++is_int_disabled_writes;
        base::on_set_is_int_disabled(f); }

    bool on_is_halted() {
        ++is_halted_reads;
        return base::on_is_halted(); }
    void on_set_is_halted(bool f) {
        ++is_halted_writes;
        base::on_set_is_halted(f); }

    void on_report() {
        std::printf("pc reads:  %10.0f\n"
                    "pc writes: %10.0f\n"
                    "sp reads:  %10.0f\n"
                    "sp writes: %10.0f\n"
                    "wz reads:  %10.0f\n"
                    "wz writes: %10.0f\n"
                    "bc reads:  %10.0f\n"
                    "bc writes: %10.0f\n"
                    "b  reads:  %10.0f\n"
                    "b  writes: %10.0f\n"
                    "c  reads:  %10.0f\n"
                    "c  writes: %10.0f\n"
                    "de reads:  %10.0f\n"
                    "de writes: %10.0f\n"
                    "d  reads:  %10.0f\n"
                    "d  writes: %10.0f\n"
                    "e  reads:  %10.0f\n"
                    "e  writes: %10.0f\n"
                    "hl reads:  %10.0f\n"
                    "hl writes: %10.0f\n"
                    "h  reads:  %10.0f\n"
                    "h  writes: %10.0f\n"
                    "l  reads:  %10.0f\n"
                    "l  writes: %10.0f\n"
                    "af reads:  %10.0f\n"
                    "af writes: %10.0f\n"
                    "a  reads:  %10.0f\n"
                    "a  writes: %10.0f\n"
                    "f  reads:  %10.0f\n"
                    "f  writes: %10.0f\n"
                    "is_int_disabled reads:  %10.0f\n"
                    "is_int_disabled writes: %10.0f\n"
                    "is_halted reads:        %10.0f\n"
                    "is_halted writes:       %10.0f\n",
                    static_cast<double>(pc_reads),
                    static_cast<double>(pc_writes),
                    static_cast<double>(sp_reads),
                    static_cast<double>(sp_writes),
                    static_cast<double>(wz_reads),
                    static_cast<double>(wz_writes),
                    static_cast<double>(bc_reads),
                    static_cast<double>(bc_writes),
                    static_cast<double>(b_reads),
                    static_cast<double>(b_writes),
                    static_cast<double>(c_reads),
                    static_cast<double>(c_writes),
                    static_cast<double>(de_reads),
                    static_cast<double>(de_writes),
                    static_cast<double>(d_reads),
                    static_cast<double>(d_writes),
                    static_cast<double>(e_reads),
                    static_cast<double>(e_writes),
                    static_cast<double>(hl_reads),
                    static_cast<double>(hl_writes),
                    static_cast<double>(h_reads),
                    static_cast<double>(h_writes),
                    static_cast<double>(l_reads),
                    static_cast<double>(l_writes),
                    static_cast<double>(af_reads),
                    static_cast<double>(af_writes),
                    static_cast<double>(a_reads),
                    static_cast<double>(a_writes),
                    static_cast<double>(f_reads),
                    static_cast<double>(f_writes),
                    static_cast<double>(is_int_disabled_reads),
                    static_cast<double>(is_int_disabled_writes),
                    static_cast<double>(is_halted_reads),
                    static_cast<double>(is_halted_writes));
    }

protected:
    using base::self;

    double pc_reads = 0;
    double pc_writes = 0;
    double sp_reads = 0;
    double sp_writes = 0;
    double wz_reads = 0;
    double wz_writes = 0;
    double bc_reads = 0;
    double bc_writes = 0;
    double b_reads = 0;
    double b_writes = 0;
    double c_reads = 0;
    double c_writes = 0;
    double de_reads = 0;
    double de_writes = 0;
    double d_reads = 0;
    double d_writes = 0;
    double e_reads = 0;
    double e_writes = 0;
    double hl_reads = 0;
    double hl_writes = 0;
    double h_reads = 0;
    double h_writes = 0;
    double l_reads = 0;
    double l_writes = 0;
    double af_reads = 0;
    double af_writes = 0;
    double a_reads = 0;
    double a_writes = 0;
    double f_reads = 0;
    double f_writes = 0;
    double is_int_disabled_reads = 0;
    double is_int_disabled_writes = 0;
    double is_halted_reads = 0;
    double is_halted_writes = 0;
};

#define WATCHER default_watcher

template<typename B>
class emulator : public WATCHER<B> {
public:
    typedef WATCHER<B> base;

    emulator() {}

    fast_u8 on_read(fast_u16 addr) {
        assert(addr < z80::address_space_size);
        return memory[addr];
    }

    void on_write(fast_u16 addr, fast_u8 n) {
        assert(addr < z80::address_space_size);
        memory[addr] = static_cast<least_u8>(n);
    }

    void run(const char *program) {
        FILE *f = std::fopen(program, "rb");
        if(!f) {
            error("Cannot open file '%s': %s", program,
                  std::strerror(errno));
        }

        std::size_t count = std::fread(
            memory + entry_addr, /* size= */ 1,
            z80::address_space_size - entry_addr, f);
        if(ferror(f)) {
            error("Cannot read file '%s': %s", program,
                  std::strerror(errno));
        }
        if(count == 0)
            error("Program file '%s' is empty", program);
        if(!feof(f))
            error("Program file '%s' is too large", program);

        if(std::fclose(f) != 0) {
            error("Cannot close file '%s': %s", program,
                  std::strerror(errno));
        }

        base::set_pc(entry_addr);
        memory[bdos_addr] = 0xc9;  // ret

        for(;;) {
            fast_u16 pc = base::get_pc();
            if(pc == quit_addr)
                break;

            self().on_step();
        }

        self().on_report();
    }

protected:
    using base::self;

private:
    least_u8 memory[z80::address_space_size] = {};
};

class i8080_emulator : public emulator<z80::i8080_cpu<i8080_emulator>>
{};

class z80_emulator : public emulator<z80::z80_cpu<z80_emulator>>
{};

[[noreturn]] static void usage() {
    error("benchmark {i8080|z80} <program.com>");
}

}  // anonymous namespace

int main(int argc, char *argv[]) {
    if(argc != 3)
        usage();

    const char *program = argv[2];

    const char *cpu = argv[1];
    if(std::strcmp(cpu, "i8080") == 0) {
        i8080_emulator e;
        e.run(program);
    } else if(std::strcmp(cpu, "z80") == 0) {
        z80_emulator e;
        e.run(program);
    } else {
        error("Unknown CPU '%s'", cpu);
    }
}
<commit_msg>Benchmark the i8080's iff.<commit_after>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <cerrno>

#include "z80.h"

namespace {

using z80::fast_u8;
using z80::fast_u16;
using z80::least_u8;
using z80::unused;

#if defined(__GNUC__) || defined(__clang__)
# define LIKE_PRINTF(format, args) \
      __attribute__((__format__(__printf__, format, args)))
#else
# define LIKE_PRINTF(format, args) /* nothing */
#endif

const char program_name[] = "benchmark";

[[noreturn]] LIKE_PRINTF(1, 0)
void verror(const char *format, va_list args) {
    std::fprintf(stderr, "%s: ", program_name);
    std::vfprintf(stderr, format, args);
    std::fprintf(stderr, "\n");
    exit(EXIT_FAILURE);
}

[[noreturn]] LIKE_PRINTF(1, 2)
void error(const char *format, ...) {
    va_list args;
    va_start(args, format);
    verror(format, args);
    va_end(args);
}

static constexpr fast_u16 quit_addr = 0x0000;
static constexpr fast_u16 bdos_addr = 0x0005;
static constexpr fast_u16 entry_addr = 0x0100;

// Handles CP/M BDOS calls to write text messages.
template<typename B>
class default_watcher : public B {
public:
    typedef B base;

    void write_char(fast_u8 c) {
        std::putchar(static_cast<char>(c));
    }

    void handle_c_write() {
        write_char(base::get_e());
    }

    void handle_c_writestr() {
        fast_u16 addr = base::get_de();
        for(;;) {
            fast_u8 c = self().on_read(addr);
            if(c == '$')
                break;

            write_char(c);
            addr = (addr + 1) % z80::address_space_size;
        }
    }

    void handle_bdos_call() {
        switch(base::get_c()) {
        case c_write:
            handle_c_write();
            break;
        case c_writestr:
            handle_c_writestr();
            break;
        }
    }

    void on_step() {
        if(base::get_pc() == bdos_addr)
            handle_bdos_call();

        base::on_step();
    }

    void on_report() {}

protected:
    using base::self;

private:
    static constexpr fast_u8 c_write = 0x02;
    static constexpr fast_u8 c_writestr = 0x09;
};

// Lets the emulator to perform at full speed.
template<typename B>
class empty_watcher : public B {
public:
    typedef B base;

    // The benchmark emulator provides no support for interrupts,
    // so no need to track the flags.
    // TODO: Remove that flag from the emulator's state at all.
    void on_set_is_int_disabled(bool f) { unused(f); }
    void on_set_iff(bool f) { unused(f); }

    void on_report() {}

protected:
    using base::self;
};

// Tracks use of CPU state.
template<typename B>
class state_watcher : public B {
public:
    typedef B base;

    fast_u16 on_get_pc() { ++pc_reads; return base::on_get_pc(); }
    void on_set_pc(fast_u16 nn) { ++pc_writes; base::on_set_pc(nn); }

    fast_u16 on_get_sp() { ++sp_reads; return base::on_get_sp(); }
    void on_set_sp(fast_u16 nn) { ++sp_writes; base::on_set_sp(nn); }

    fast_u16 on_get_wz() { ++wz_reads; return base::on_get_wz(); }
    void on_set_wz(fast_u16 nn) { ++wz_writes; base::on_set_wz(nn); }

    fast_u16 on_get_bc() { ++bc_reads; return base::on_get_bc(); }
    void on_set_bc(fast_u16 nn) { ++bc_writes; base::on_set_bc(nn); }
    fast_u8 on_get_b() { ++b_reads; return base::on_get_b(); }
    void on_set_b(fast_u8 n) { ++b_writes; base::on_set_b(n); }
    fast_u8 on_get_c() { ++c_reads; return base::on_get_c(); }
    void on_set_c(fast_u8 n) { ++c_writes; base::on_set_c(n); }

    fast_u16 on_get_de() { ++de_reads; return base::on_get_de(); }
    void on_set_de(fast_u16 nn) { ++de_writes; base::on_set_de(nn); }
    fast_u8 on_get_d() { ++d_reads; return base::on_get_d(); }
    void on_set_d(fast_u8 n) { ++d_writes; base::on_set_d(n); }
    fast_u8 on_get_e() { ++e_reads; return base::on_get_e(); }
    void on_set_e(fast_u8 n) { ++e_writes; base::on_set_e(n); }

    fast_u16 on_get_hl() { ++hl_reads; return base::on_get_hl(); }
    void on_set_hl(fast_u16 nn) { ++hl_writes; base::on_set_hl(nn); }
    fast_u8 on_get_h() { ++h_reads; return base::on_get_h(); }
    void on_set_h(fast_u8 n) { ++h_writes; base::on_set_h(n); }
    fast_u8 on_get_l() { ++l_reads; return base::on_get_l(); }
    void on_set_l(fast_u8 n) { ++l_writes; base::on_set_l(n); }

    fast_u16 on_get_af() { ++af_reads; return base::on_get_af(); }
    void on_set_af(fast_u16 nn) { ++af_writes; base::on_set_af(nn); }
    fast_u8 on_get_a() { ++a_reads; return base::on_get_a(); }
    void on_set_a(fast_u8 n) { ++a_writes; base::on_set_a(n); }
    fast_u8 on_get_f() { ++f_reads; return base::on_get_f(); }
    void on_set_f(fast_u8 n) { ++f_writes; base::on_set_f(n); }

    bool on_is_int_disabled() {
        ++is_int_disabled_reads;
        return base::on_is_int_disabled(); }
    void on_set_is_int_disabled(bool f) {
        ++is_int_disabled_writes;
        base::on_set_is_int_disabled(f); }

    bool on_is_halted() {
        ++is_halted_reads;
        return base::on_is_halted(); }
    void on_set_is_halted(bool f) {
        ++is_halted_writes;
        base::on_set_is_halted(f); }

    bool on_get_iff() { ++iff_reads; return base::on_get_iff(); }
    void on_set_iff(bool f) { ++iff_writes; base::on_set_iff(f); }

    void on_report() {
        std::printf("             pc reads:  %10.0f\n"
                    "             pc writes: %10.0f\n"
                    "             sp reads:  %10.0f\n"
                    "             sp writes: %10.0f\n"
                    "             wz reads:  %10.0f\n"
                    "             wz writes: %10.0f\n"
                    "             bc reads:  %10.0f\n"
                    "             bc writes: %10.0f\n"
                    "              b reads:  %10.0f\n"
                    "              b writes: %10.0f\n"
                    "              c reads:  %10.0f\n"
                    "              c writes: %10.0f\n"
                    "             de reads:  %10.0f\n"
                    "             de writes: %10.0f\n"
                    "              d reads:  %10.0f\n"
                    "              d writes: %10.0f\n"
                    "              e reads:  %10.0f\n"
                    "              e writes: %10.0f\n"
                    "             hl reads:  %10.0f\n"
                    "             hl writes: %10.0f\n"
                    "              h reads:  %10.0f\n"
                    "              h writes: %10.0f\n"
                    "              l reads:  %10.0f\n"
                    "              l writes: %10.0f\n"
                    "             af reads:  %10.0f\n"
                    "             af writes: %10.0f\n"
                    "              a reads:  %10.0f\n"
                    "              a writes: %10.0f\n"
                    "              f reads:  %10.0f\n"
                    "              f writes: %10.0f\n"
                    "            iff reads:  %10.0f\n"
                    "            iff writes: %10.0f\n"
                    "is_int_disabled reads:  %10.0f\n"
                    "is_int_disabled writes: %10.0f\n"
                    "      is_halted reads:  %10.0f\n"
                    "      is_halted writes: %10.0f\n",
                    static_cast<double>(pc_reads),
                    static_cast<double>(pc_writes),
                    static_cast<double>(sp_reads),
                    static_cast<double>(sp_writes),
                    static_cast<double>(wz_reads),
                    static_cast<double>(wz_writes),
                    static_cast<double>(bc_reads),
                    static_cast<double>(bc_writes),
                    static_cast<double>(b_reads),
                    static_cast<double>(b_writes),
                    static_cast<double>(c_reads),
                    static_cast<double>(c_writes),
                    static_cast<double>(de_reads),
                    static_cast<double>(de_writes),
                    static_cast<double>(d_reads),
                    static_cast<double>(d_writes),
                    static_cast<double>(e_reads),
                    static_cast<double>(e_writes),
                    static_cast<double>(hl_reads),
                    static_cast<double>(hl_writes),
                    static_cast<double>(h_reads),
                    static_cast<double>(h_writes),
                    static_cast<double>(l_reads),
                    static_cast<double>(l_writes),
                    static_cast<double>(af_reads),
                    static_cast<double>(af_writes),
                    static_cast<double>(a_reads),
                    static_cast<double>(a_writes),
                    static_cast<double>(f_reads),
                    static_cast<double>(f_writes),
                    static_cast<double>(iff_reads),
                    static_cast<double>(iff_writes),
                    static_cast<double>(is_int_disabled_reads),
                    static_cast<double>(is_int_disabled_writes),
                    static_cast<double>(is_halted_reads),
                    static_cast<double>(is_halted_writes));
    }

protected:
    using base::self;

    double pc_reads = 0;
    double pc_writes = 0;
    double sp_reads = 0;
    double sp_writes = 0;
    double wz_reads = 0;
    double wz_writes = 0;
    double bc_reads = 0;
    double bc_writes = 0;
    double b_reads = 0;
    double b_writes = 0;
    double c_reads = 0;
    double c_writes = 0;
    double de_reads = 0;
    double de_writes = 0;
    double d_reads = 0;
    double d_writes = 0;
    double e_reads = 0;
    double e_writes = 0;
    double hl_reads = 0;
    double hl_writes = 0;
    double h_reads = 0;
    double h_writes = 0;
    double l_reads = 0;
    double l_writes = 0;
    double af_reads = 0;
    double af_writes = 0;
    double a_reads = 0;
    double a_writes = 0;
    double f_reads = 0;
    double f_writes = 0;
    double iff_reads = 0;
    double iff_writes = 0;
    double is_int_disabled_reads = 0;
    double is_int_disabled_writes = 0;
    double is_halted_reads = 0;
    double is_halted_writes = 0;
};

#define WATCHER default_watcher

template<typename B>
class emulator : public WATCHER<B> {
public:
    typedef WATCHER<B> base;

    emulator() {}

    fast_u8 on_read(fast_u16 addr) {
        assert(addr < z80::address_space_size);
        return memory[addr];
    }

    void on_write(fast_u16 addr, fast_u8 n) {
        assert(addr < z80::address_space_size);
        memory[addr] = static_cast<least_u8>(n);
    }

    void run(const char *program) {
        FILE *f = std::fopen(program, "rb");
        if(!f) {
            error("Cannot open file '%s': %s", program,
                  std::strerror(errno));
        }

        std::size_t count = std::fread(
            memory + entry_addr, /* size= */ 1,
            z80::address_space_size - entry_addr, f);
        if(ferror(f)) {
            error("Cannot read file '%s': %s", program,
                  std::strerror(errno));
        }
        if(count == 0)
            error("Program file '%s' is empty", program);
        if(!feof(f))
            error("Program file '%s' is too large", program);

        if(std::fclose(f) != 0) {
            error("Cannot close file '%s': %s", program,
                  std::strerror(errno));
        }

        base::set_pc(entry_addr);
        memory[bdos_addr] = 0xc9;  // ret

        for(;;) {
            fast_u16 pc = base::get_pc();
            if(pc == quit_addr)
                break;

            self().on_step();
        }

        self().on_report();
    }

protected:
    using base::self;

private:
    least_u8 memory[z80::address_space_size] = {};
};

class i8080_emulator : public emulator<z80::i8080_cpu<i8080_emulator>>
{};

class z80_emulator : public emulator<z80::z80_cpu<z80_emulator>>
{};

[[noreturn]] static void usage() {
    error("benchmark {i8080|z80} <program.com>");
}

}  // anonymous namespace

int main(int argc, char *argv[]) {
    if(argc != 3)
        usage();

    const char *program = argv[2];

    const char *cpu = argv[1];
    if(std::strcmp(cpu, "i8080") == 0) {
        i8080_emulator e;
        e.run(program);
    } else if(std::strcmp(cpu, "z80") == 0) {
        z80_emulator e;
        e.run(program);
    } else {
        error("Unknown CPU '%s'", cpu);
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov 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.
//
// uncov 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 uncov.  If not, see <http://www.gnu.org/licenses/>.

#include "SubCommand.hpp"

#include <cstdlib>

#include <iostream>
#include <string>
#include <vector>

std::vector<SubCommand *>
SubCommand::getAll()
{
    std::vector<SubCommand *> cmds;
    cmds.reserve(getAllCmds().size());
    for (const auto &cmd : getAllCmds()) {
        cmds.push_back(cmd.get());
    }
    return cmds;
}

std::vector<std::unique_ptr<SubCommand>> &
SubCommand::getAllCmds()
{
    static std::vector<std::unique_ptr<SubCommand>> allCmds;
    return allCmds;
}

int
SubCommand::exec(BuildHistory &bh, Repository &repo,
                 const std::vector<std::string> &args)
{
    hasErrors = false;

    if (args.size() < minArgs) {
        std::cout << "Too few subcommand arguments: " << args.size() << ".  "
                  << makeExpectedMsg() << '\n';
        error();
    } else if (args.size() > maxArgs) {
        std::cout << "Too many subcommand arguments: " << args.size() << ".  "
                  << makeExpectedMsg() << '\n';
        error();
    }

    if (!hasErrors) {
        bhValue = &bh;
        repoValue = &repo;

        execImpl(args);
    }

    return isFailed() ? EXIT_FAILURE : EXIT_SUCCESS;
}

void
SubCommand::error()
{
    hasErrors = true;
}

std::string
SubCommand::makeExpectedMsg() const
{
    if (minArgs == maxArgs) {
        return "Expected exactly " + std::to_string(minArgs) + '.';
    }
    return "Expected at least " + std::to_string(minArgs) + " and at most "
         + std::to_string(maxArgs) + '.';
}
<commit_msg>Consistent use of isFailed/hasErrors in SubCommand<commit_after>// Copyright (C) 2016 xaizek <xaizek@openmailbox.org>
//
// This file is part of uncov.
//
// uncov 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.
//
// uncov 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 uncov.  If not, see <http://www.gnu.org/licenses/>.

#include "SubCommand.hpp"

#include <cstdlib>

#include <iostream>
#include <string>
#include <vector>

std::vector<SubCommand *>
SubCommand::getAll()
{
    std::vector<SubCommand *> cmds;
    cmds.reserve(getAllCmds().size());
    for (const auto &cmd : getAllCmds()) {
        cmds.push_back(cmd.get());
    }
    return cmds;
}

std::vector<std::unique_ptr<SubCommand>> &
SubCommand::getAllCmds()
{
    static std::vector<std::unique_ptr<SubCommand>> allCmds;
    return allCmds;
}

int
SubCommand::exec(BuildHistory &bh, Repository &repo,
                 const std::vector<std::string> &args)
{
    hasErrors = false;

    if (args.size() < minArgs) {
        std::cout << "Too few subcommand arguments: " << args.size() << ".  "
                  << makeExpectedMsg() << '\n';
        error();
    } else if (args.size() > maxArgs) {
        std::cout << "Too many subcommand arguments: " << args.size() << ".  "
                  << makeExpectedMsg() << '\n';
        error();
    }

    if (!hasErrors) {
        bhValue = &bh;
        repoValue = &repo;

        execImpl(args);
    }

    return hasErrors ? EXIT_FAILURE : EXIT_SUCCESS;
}

void
SubCommand::error()
{
    hasErrors = true;
}

std::string
SubCommand::makeExpectedMsg() const
{
    if (minArgs == maxArgs) {
        return "Expected exactly " + std::to_string(minArgs) + '.';
    }
    return "Expected at least " + std::to_string(minArgs) + " and at most "
         + std::to_string(maxArgs) + '.';
}
<|endoftext|>