<commit_before>/** \copyright
 * Copyright (c) 2014, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file Stm32Can.cxx
 *
 * This file implements a CAN driver for the STM32F10x microcontrollers using
 * the STM32 peripheral library.
 *
 * @author Balazs Racz
 * @date 15 June 2014
 */

#include "stm32f10x.h"
#include "Can.hxx"

#define USE_OLIMEXINO_STM32

#ifdef USE_OLIMEXINO_STM32
#define RCC_APB2Periph_GPIO_CAN1 RCC_APB2Periph_GPIOB
#define GPIO_Remapping_CAN1 GPIO_Remap1_CAN1
#define GPIO_CAN1 GPIOB
#define GPIO_Pin_CAN1_RX GPIO_Pin_8
#define GPIO_Pin_CAN1_TX GPIO_Pin_9
#endif

extern "C" {
void USB_HP_CAN1_TX_IRQHandler(void);
void USB_LP_CAN1_RX0_IRQHandler(void);
}

class Stm32CanDriver : public Can
{
public:
    Stm32CanDriver(CAN_TypeDef *instance, const char *dev)
        : Can(dev)
        , instance_(instance)
    {
        HASSERT(instance == CAN1);
        init_can_gpio();
        init_can_ctrl();
    }

private:
    void init_can_gpio()
    {
        // Turns on clock for the GPIO port of the canbus.
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_CAN1, ENABLE);

        // Configures CAN RX pin
        GPIO_InitTypeDef gp;
        GPIO_StructInit(&gp);
        gp.GPIO_Pin = GPIO_Pin_CAN1_RX;
        gp.GPIO_Mode = GPIO_Mode_IPU;
        GPIO_Init(GPIO_CAN1, &gp);

        // Configures CAN TX pin
        gp.GPIO_Pin = GPIO_Pin_CAN1_TX;
        gp.GPIO_Mode = GPIO_Mode_AF_PP;
        gp.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIO_CAN1, &gp);

        GPIO_PinRemapConfig(GPIO_Remapping_CAN1, ENABLE);

        // Turns on clock for the CAN controller
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    }

    void init_can_ctrl()
    {
        CAN_DeInit(instance_);

        CAN_InitTypeDef CAN_InitStructure;
        CAN_StructInit(&CAN_InitStructure);
        CAN_InitStructure.CAN_TTCM = DISABLE;
        CAN_InitStructure.CAN_ABOM = DISABLE;
        CAN_InitStructure.CAN_AWUM = DISABLE;
        CAN_InitStructure.CAN_NART = DISABLE;
        CAN_InitStructure.CAN_RFLM = DISABLE;
        CAN_InitStructure.CAN_TXFP = DISABLE;
        CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;

        /* CAN Baudrate = 1MBps / requested_rate*/
        CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
        CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;
        CAN_InitStructure.CAN_BS2 = CAN_BS2_5tq;
        CAN_InitStructure.CAN_Prescaler =
            4 * 1000000 / config_nmranet_can_bitrate();
        HASSERT(4 * 1000000 / CAN_InitStructure.CAN_Prescaler ==
                config_nmranet_can_bitrate());
        HASSERT(CAN_Init(instance_, &CAN_InitStructure) ==
                CAN_InitStatus_Success);
    }

    void init_can_filter()
    {
        CAN_FilterInitTypeDef CAN_FilterInitStructure;
        CAN_FilterInitStructure.CAN_FilterNumber = 0;
        CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
        CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
        CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
        CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
        CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
        CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
        CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
        CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
        CAN_FilterInit(&CAN_FilterInitStructure);
    }

    void enable() OVERRIDE
    {
        init_can_filter();
        // Sets the CAN interrupt priorities to be compatible with FreeRTOS.
        NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, configKERNEL_INTERRUPT_PRIORITY);
        NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, configKERNEL_INTERRUPT_PRIORITY);
        CAN_ITConfig(instance_, CAN_IT_TME, DISABLE);
        NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
        NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
        CAN_ITConfig(instance_, CAN_IT_FMP0, ENABLE);
    };
    void disable() OVERRIDE
    {
        CAN_ITConfig(instance_, CAN_IT_FMP0, DISABLE);
        CAN_ITConfig(instance_, CAN_IT_TME, DISABLE);
        NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
        NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
    };

    void tx_interrupt();
    void rx_interrupt();
    void tx_msg() OVERRIDE;

    friend void USB_HP_CAN1_TX_IRQHandler(void);
    friend void USB_LP_CAN1_RX0_IRQHandler(void);

    CAN_TypeDef *instance_;

    static const uint32_t CAN_TSR_TME_ANY =
        CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2;

    /** Converts between our frame format and STM32 peripheral library's frame
     * format. */
    static void fill_tx_message(const struct can_frame &frame, CanTxMsg *msg);
    /** Converts between our frame format and STM32 peripheral library's frame
     * format. */
    static void parse_rx_message(const CanRxMsg &msg, struct can_frame *frame);
};

// static
void Stm32CanDriver::fill_tx_message(const struct can_frame &frame,
                                     CanTxMsg *msg)
{
    if (IS_CAN_FRAME_EFF(frame))
    {
        msg->IDE = CAN_Id_Extended;
        msg->ExtId = GET_CAN_FRAME_ID_EFF(frame);
    }
    else
    {
        msg->IDE = CAN_Id_Standard;
        msg->StdId = GET_CAN_FRAME_ID(frame);
    }
    if (IS_CAN_FRAME_RTR(frame))
    {
        msg->RTR = CAN_RTR_Remote;
    }
    else
    {
        msg->RTR = CAN_RTR_Data;
    }
    msg->DLC = frame.can_dlc;
    memcpy(msg->Data, frame.data, frame.can_dlc);
}

// static
void Stm32CanDriver::parse_rx_message(const CanRxMsg &msg,
                                      struct can_frame *frame)
{
    if (msg.IDE == CAN_Id_Standard)
    {
        CLR_CAN_FRAME_EFF(*frame);
        SET_CAN_FRAME_ID(*frame, msg.StdId);
    }
    else
    {
        SET_CAN_FRAME_EFF(*frame);
        SET_CAN_FRAME_ID_EFF(*frame, msg.ExtId);
    }
    if (msg.RTR == CAN_RTR_Data)
    {
        CLR_CAN_FRAME_RTR(*frame);
    }
    else
    {
        SET_CAN_FRAME_RTR(*frame);
    }
    CLR_CAN_FRAME_ERR(*frame);
    frame->can_dlc = msg.DLC;
    memcpy(frame->data, msg.Data, msg.DLC);
}

/** Try and transmit a message. Does nothing if there is no message to transmit
 *  or no write buffers to transmit via.
 * @param dev device to transmit message on
 */
void Stm32CanDriver::tx_msg()
{
    if (instance_->IER & CAN_IT_TME)
    {
        // We are waiting for a transmit interrupt to come in. That interrupt
        // will send off our frame.
        return;
    }
    // Now: no transmit interrupt is pending. This means that there must be a
    // transmit message buffer free.
    HASSERT(instance_->TSR & CAN_TSR_TME_ANY);

    struct can_frame* can_frame;
    size_t msg_count = txBuf->data_read_pointer(&can_frame);
    if (!msg_count)
    {
        // No frame -- probably our frame was sent off by a racing ISR.
        return;
    }
    CanTxMsg msg;
    fill_tx_message(can_frame[0], &msg);
    if (CAN_Transmit(instance_, &msg) == CAN_TxStatus_NoMailBox)
    {
        // the tx interrupt will send off the frame.
        return;
    }
    else
    {
        txBuf->consume(1);
        txBuf->signal_condition();
    }
    if ((instance_->TSR & CAN_TSR_TME_ANY) == 0)
    {
        // No more free buffers --> enable transmit IRQ.
        instance_->IER |= CAN_IT_TME;
    }
}

/** Handler for CAN device's transmit interrupt. */
void Stm32CanDriver::tx_interrupt()
{
    while (instance_->TSR & CAN_TSR_TME_ANY)
    {
        struct can_frame* can_frame;
        size_t msg_count = txBuf->data_read_pointer(&can_frame);
        if (!msg_count)
        {
            // No new frame but we have a txbuffer -> disable tx interrupt.
            instance_->IER &= ~CAN_IT_TME;
            return;
        }
        CanTxMsg msg;
        fill_tx_message(can_frame[0], &msg);
        if (CAN_Transmit(instance_, &msg) == CAN_TxStatus_NoMailBox)
        {
            DIE("Internal inconsistency: free xmit buffer disappeared.");
        }
        else
        {
            txBuf->consume(1);
            txBuf->signal_condition_from_isr();
        }
    }
}

void Stm32CanDriver::rx_interrupt()
{
    HASSERT(instance_->RF0R & 3);
    struct can_frame *can_frame;
    while (instance_->RF0R & 3 && rxBuf->data_write_pointer(&can_frame)) {
        CanRxMsg msg;
        CAN_Receive(instance_, CAN_FIFO0, &msg);
        parse_rx_message(msg, can_frame);
        rxBuf->advance(1);
        rxBuf->signal_condition_from_isr();
    }
    /** @TODO: if there is no rx buf left, we need to disable the rx interrupt
        or else we get stuck in an infinite interrupt loop. */
}

Stm32CanDriver can0(CAN1, "/dev/can0");

extern "C" {

void USB_HP_CAN1_TX_IRQHandler(void) {
    can0.tx_interrupt();
}

void USB_LP_CAN1_RX0_IRQHandler(void) {
    can0.rx_interrupt();
}

}
<commit_msg>Adds notify support for the STM32F1 CAN driver.<commit_after>/** \copyright
 * Copyright (c) 2014, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file Stm32Can.cxx
 *
 * This file implements a CAN driver for the STM32F10x microcontrollers using
 * the STM32 peripheral library.
 *
 * @author Balazs Racz
 * @date 15 June 2014
 */

#include "stm32f10x.h"
#include "Can.hxx"

#define USE_OLIMEXINO_STM32

#ifdef USE_OLIMEXINO_STM32
#define RCC_APB2Periph_GPIO_CAN1 RCC_APB2Periph_GPIOB
#define GPIO_Remapping_CAN1 GPIO_Remap1_CAN1
#define GPIO_CAN1 GPIOB
#define GPIO_Pin_CAN1_RX GPIO_Pin_8
#define GPIO_Pin_CAN1_TX GPIO_Pin_9
#endif

extern "C" {
void USB_HP_CAN1_TX_IRQHandler(void);
void USB_LP_CAN1_RX0_IRQHandler(void);
}

class Stm32CanDriver : public Can
{
public:
    Stm32CanDriver(CAN_TypeDef *instance, const char *dev)
        : Can(dev)
        , instance_(instance)
    {
        HASSERT(instance == CAN1);
        init_can_gpio();
        init_can_ctrl();
    }

private:
    void init_can_gpio()
    {
        // Turns on clock for the GPIO port of the canbus.
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_CAN1, ENABLE);

        // Configures CAN RX pin
        GPIO_InitTypeDef gp;
        GPIO_StructInit(&gp);
        gp.GPIO_Pin = GPIO_Pin_CAN1_RX;
        gp.GPIO_Mode = GPIO_Mode_IPU;
        GPIO_Init(GPIO_CAN1, &gp);

        // Configures CAN TX pin
        gp.GPIO_Pin = GPIO_Pin_CAN1_TX;
        gp.GPIO_Mode = GPIO_Mode_AF_PP;
        gp.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIO_CAN1, &gp);

        GPIO_PinRemapConfig(GPIO_Remapping_CAN1, ENABLE);

        // Turns on clock for the CAN controller
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    }

    void init_can_ctrl()
    {
        CAN_DeInit(instance_);

        CAN_InitTypeDef CAN_InitStructure;
        CAN_StructInit(&CAN_InitStructure);
        CAN_InitStructure.CAN_TTCM = DISABLE;
        CAN_InitStructure.CAN_ABOM = DISABLE;
        CAN_InitStructure.CAN_AWUM = DISABLE;
        CAN_InitStructure.CAN_NART = DISABLE;
        CAN_InitStructure.CAN_RFLM = DISABLE;
        CAN_InitStructure.CAN_TXFP = DISABLE;
        CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;

        /* CAN Baudrate = 1MBps / requested_rate*/
        CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
        CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;
        CAN_InitStructure.CAN_BS2 = CAN_BS2_5tq;
        CAN_InitStructure.CAN_Prescaler =
            4 * 1000000 / config_nmranet_can_bitrate();
        HASSERT(4 * 1000000 / CAN_InitStructure.CAN_Prescaler ==
                config_nmranet_can_bitrate());
        HASSERT(CAN_Init(instance_, &CAN_InitStructure) ==
                CAN_InitStatus_Success);
    }

    void init_can_filter()
    {
        CAN_FilterInitTypeDef CAN_FilterInitStructure;
        CAN_FilterInitStructure.CAN_FilterNumber = 0;
        CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
        CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
        CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
        CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
        CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
        CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
        CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
        CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
        CAN_FilterInit(&CAN_FilterInitStructure);
    }

    void enable() OVERRIDE
    {
        init_can_filter();
        // Sets the CAN interrupt priorities to be compatible with FreeRTOS.
        NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, configKERNEL_INTERRUPT_PRIORITY);
        NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, configKERNEL_INTERRUPT_PRIORITY);
        CAN_ITConfig(instance_, CAN_IT_TME, DISABLE);
        NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
        NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
        CAN_ITConfig(instance_, CAN_IT_FMP0, ENABLE);
    };
    void disable() OVERRIDE
    {
        CAN_ITConfig(instance_, CAN_IT_FMP0, DISABLE);
        CAN_ITConfig(instance_, CAN_IT_TME, DISABLE);
        NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
        NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
    };

    void tx_interrupt();
    void rx_interrupt();
    void tx_msg() OVERRIDE;

    friend void USB_HP_CAN1_TX_IRQHandler(void);
    friend void USB_LP_CAN1_RX0_IRQHandler(void);

    CAN_TypeDef *instance_;

    static const uint32_t CAN_TSR_TME_ANY =
        CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2;

    /** Converts between our frame format and STM32 peripheral library's frame
     * format. */
    static void fill_tx_message(const struct can_frame &frame, CanTxMsg *msg);
    /** Converts between our frame format and STM32 peripheral library's frame
     * format. */
    static void parse_rx_message(const CanRxMsg &msg, struct can_frame *frame);
};

// static
void Stm32CanDriver::fill_tx_message(const struct can_frame &frame,
                                     CanTxMsg *msg)
{
    if (IS_CAN_FRAME_EFF(frame))
    {
        msg->IDE = CAN_Id_Extended;
        msg->ExtId = GET_CAN_FRAME_ID_EFF(frame);
    }
    else
    {
        msg->IDE = CAN_Id_Standard;
        msg->StdId = GET_CAN_FRAME_ID(frame);
    }
    if (IS_CAN_FRAME_RTR(frame))
    {
        msg->RTR = CAN_RTR_Remote;
    }
    else
    {
        msg->RTR = CAN_RTR_Data;
    }
    msg->DLC = frame.can_dlc;
    memcpy(msg->Data, frame.data, frame.can_dlc);
}

// static
void Stm32CanDriver::parse_rx_message(const CanRxMsg &msg,
                                      struct can_frame *frame)
{
    if (msg.IDE == CAN_Id_Standard)
    {
        CLR_CAN_FRAME_EFF(*frame);
        SET_CAN_FRAME_ID(*frame, msg.StdId);
    }
    else
    {
        SET_CAN_FRAME_EFF(*frame);
        SET_CAN_FRAME_ID_EFF(*frame, msg.ExtId);
    }
    if (msg.RTR == CAN_RTR_Data)
    {
        CLR_CAN_FRAME_RTR(*frame);
    }
    else
    {
        SET_CAN_FRAME_RTR(*frame);
    }
    CLR_CAN_FRAME_ERR(*frame);
    frame->can_dlc = msg.DLC;
    memcpy(frame->data, msg.Data, msg.DLC);
}

/** Try and transmit a message. Does nothing if there is no message to transmit
 *  or no write buffers to transmit via.
 * @param dev device to transmit message on
 */
void Stm32CanDriver::tx_msg()
{
    if (instance_->IER & CAN_IT_TME)
    {
        // We are waiting for a transmit interrupt to come in. That interrupt
        // will send off our frame.
        return;
    }
    // Now: no transmit interrupt is pending. This means that there must be a
    // transmit message buffer free.
    HASSERT(instance_->TSR & CAN_TSR_TME_ANY);

    struct can_frame* can_frame;
    size_t msg_count = txBuf->data_read_pointer(&can_frame);
    if (!msg_count)
    {
        // No frame -- probably our frame was sent off by a racing ISR.
        return;
    }
    CanTxMsg msg;
    fill_tx_message(can_frame[0], &msg);
    if (CAN_Transmit(instance_, &msg) == CAN_TxStatus_NoMailBox)
    {
        // the tx interrupt will send off the frame.
        return;
    }
    else
    {
        txBuf->consume(1);
        txBuf->signal_condition();
        if (writableNotify_)
        {
            writableNotify_->notify();
            writableNotify_= nullptr;
        }
    }
    if ((instance_->TSR & CAN_TSR_TME_ANY) == 0)
    {
        // No more free buffers --> enable transmit IRQ.
        instance_->IER |= CAN_IT_TME;
    }
}

/** Handler for CAN device's transmit interrupt. */
void Stm32CanDriver::tx_interrupt()
{
    while (instance_->TSR & CAN_TSR_TME_ANY)
    {
        struct can_frame* can_frame;
        size_t msg_count = txBuf->data_read_pointer(&can_frame);
        if (!msg_count)
        {
            // No new frame but we have a txbuffer -> disable tx interrupt.
            instance_->IER &= ~CAN_IT_TME;
            return;
        }
        CanTxMsg msg;
        fill_tx_message(can_frame[0], &msg);
        if (CAN_Transmit(instance_, &msg) == CAN_TxStatus_NoMailBox)
        {
            DIE("Internal inconsistency: free xmit buffer disappeared.");
        }
        else
        {
            txBuf->consume(1);
            txBuf->signal_condition_from_isr();
            if (writableNotify_)
            {
                writableNotify_->notify_from_isr();
                writableNotify_= nullptr;
            }
        }
    }
}

void Stm32CanDriver::rx_interrupt()
{
    HASSERT(instance_->RF0R & 3);
    struct can_frame *can_frame;
    while (instance_->RF0R & 3 && rxBuf->data_write_pointer(&can_frame)) {
        CanRxMsg msg;
        CAN_Receive(instance_, CAN_FIFO0, &msg);
        parse_rx_message(msg, can_frame);
        rxBuf->advance(1);
        rxBuf->signal_condition_from_isr();
        if (readableNotify_)
        {
            readableNotify_->notify_from_isr();
            readableNotify_ = nullptr;
        }
    }
    /** @TODO: if there is no rx buf left, we need to disable the rx interrupt
        or else we get stuck in an infinite interrupt loop. */
}

Stm32CanDriver can0(CAN1, "/dev/can0");

extern "C" {

void USB_HP_CAN1_TX_IRQHandler(void) {
    can0.tx_interrupt();
}

void USB_LP_CAN1_RX0_IRQHandler(void) {
    can0.rx_interrupt();
}

}
<|endoftext|>


<commit_before>#include "engine/Engine.hpp"
#include "settings.hpp"
#include "system.hpp"

#include <algorithm>
#include <chrono>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <omp.h>
#include <xmmintrin.h>
#include <pmmintrin.h>

namespace rack {

/** Threads which obtain a VIPLock will cause wait() to block for other less important threads.
This does not provide the VIPs with an exclusive lock. That should be left up to another mutex shared between the less important thread.
*/
struct VIPMutex {
	int count = 0;
	std::condition_variable cv;
	std::mutex countMutex;

	/** Blocks until there are no remaining VIPLocks */
	void wait() {
		std::unique_lock<std::mutex> lock(countMutex);
		while (count > 0)
			cv.wait(lock);
	}
};

struct VIPLock {
	VIPMutex &m;
	VIPLock(VIPMutex &m) : m(m) {
		std::unique_lock<std::mutex> lock(m.countMutex);
		m.count++;
	}
	~VIPLock() {
		std::unique_lock<std::mutex> lock(m.countMutex);
		m.count--;
		lock.unlock();
		m.cv.notify_all();
	}
};

struct Engine::Internal {
	bool running = false;
	float sampleRate;
	float sampleTime;
	float sampleRateRequested;

	int nextModuleId = 1;
	int nextCableId = 1;

	// Parameter smoothing
	Module *smoothModule = NULL;
	int smoothParamId;
	float smoothValue;

	std::mutex mutex;
	std::thread thread;
	VIPMutex vipMutex;
};

Engine::Engine() {
	internal = new Internal;

	float sampleRate = 44100.f;
	internal->sampleRate = sampleRate;
	internal->sampleTime = 1 / sampleRate;
	internal->sampleRateRequested = sampleRate;

	threadCount = 1;
}

Engine::~Engine() {
	// Make sure there are no cables or modules in the rack on destruction. This suggests that a module failed to remove itself before the RackWidget was destroyed.
	assert(cables.empty());
	assert(modules.empty());

	delete internal;
}

static void Engine_step(Engine *engine) {
	// Sample rate
	if (engine->internal->sampleRateRequested != engine->internal->sampleRate) {
		engine->internal->sampleRate = engine->internal->sampleRateRequested;
		engine->internal->sampleTime = 1 / engine->internal->sampleRate;
		for (Module *module : engine->modules) {
			module->onSampleRateChange();
		}
	}

	// Param smoothing
	{
		Module *smoothModule = engine->internal->smoothModule;
		int smoothParamId = engine->internal->smoothParamId;
		float smoothValue = engine->internal->smoothValue;
		if (smoothModule) {
			Param *param = &smoothModule->params[smoothParamId];
			float value = param->value;
			// decay rate is 1 graphics frame
			const float smoothLambda = 60.f;
			float newValue = value + (smoothValue - value) * smoothLambda * engine->internal->sampleTime;
			if (value == newValue || !(param->minValue <= newValue && newValue <= param->maxValue)) {
				// Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats), or if newValue is out of bounds
				param->setValue(smoothValue);
				engine->internal->smoothModule = NULL;
			}
			else {
				param->value = newValue;
			}
		}
	}

	float cpuLambda = engine->internal->sampleTime / 2.f;

	// Iterate modules
	int modulesLen = engine->modules.size();
	#pragma omp parallel for num_threads(engine->threadCount) schedule(dynamic)
	for (int i = 0; i < modulesLen; i++) {
		Module *module = engine->modules[i];
		if (!module->bypass) {
			// Step module
			if (settings::powerMeter) {
				auto startTime = std::chrono::high_resolution_clock::now();

				module->step();

				auto stopTime = std::chrono::high_resolution_clock::now();
				float cpuTime = std::chrono::duration<float>(stopTime - startTime).count();
				// Smooth cpu time
				module->cpuTime += (cpuTime - module->cpuTime) * cpuLambda;
			}
			else {
				module->step();
			}
		}

		// Iterate ports and step plug lights
		for (Input &input : module->inputs) {
			input.step();
		}
		for (Output &output : module->outputs) {
			output.step();
		}
	}

	// Step cables
	for (Cable *cable : engine->cables) {
		cable->step();
	}
}

static void Engine_run(Engine *engine) {
#if defined ARCH_LIN
	// Name thread
	system::setThreadName("Engine");
#endif
	// Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
	// https://software.intel.com/en-us/node/682949
	_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
	_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);

	// Every time the engine waits and locks a mutex, it steps this many frames
	const int mutexSteps = 64;
	// Time in seconds that the engine is rushing ahead of the estimated clock time
	double ahead = 0.0;
	auto lastTime = std::chrono::high_resolution_clock::now();

	while (engine->internal->running) {
		engine->internal->vipMutex.wait();

		if (!engine->paused) {
			std::lock_guard<std::mutex> lock(engine->internal->mutex);
			// auto startTime = std::chrono::high_resolution_clock::now();

			for (int i = 0; i < mutexSteps; i++) {
				Engine_step(engine);
			}

			// auto stopTime = std::chrono::high_resolution_clock::now();
			// float cpuTime = std::chrono::duration<float>(stopTime - startTime).count();
			// DEBUG("%g", cpuTime / mutexSteps * 44100);
		}

		double stepTime = mutexSteps * engine->internal->sampleTime;
		ahead += stepTime;
		auto currTime = std::chrono::high_resolution_clock::now();
		const double aheadFactor = 2.0;
		ahead -= aheadFactor * std::chrono::duration<double>(currTime - lastTime).count();
		lastTime = currTime;
		ahead = std::fmax(ahead, 0.0);

		// Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate
		// The number of steps to wait before possibly sleeping
		const double aheadMax = 1.0; // seconds
		if (ahead > aheadMax) {
			std::this_thread::sleep_for(std::chrono::duration<double>(stepTime));
		}
	}
}

void Engine::start() {
	internal->running = true;
	internal->thread = std::thread(Engine_run, this);
}

void Engine::stop() {
	internal->running = false;
	internal->thread.join();
}

void Engine::addModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// Check that the module is not already added
	auto it = std::find(modules.begin(), modules.end(), module);
	assert(it == modules.end());
	// Set ID
	if (module->id == 0) {
		// Automatically assign ID
		module->id = internal->nextModuleId++;
	}
	else {
		// Manual ID
		assert(module->id < internal->nextModuleId);
		// Check that the ID is not already taken
		for (Module *m : modules) {
			assert(module->id != m->id);
		}
	}
	// Add module
	modules.push_back(module);
}

void Engine::removeModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// If a param is being smoothed on this module, stop smoothing it immediately
	if (module == internal->smoothModule) {
		internal->smoothModule = NULL;
	}
	// Check that all cables are disconnected
	for (Cable *cable : cables) {
		assert(cable->outputModule != module);
		assert(cable->inputModule != module);
	}
	// Check that the module actually exists
	auto it = std::find(modules.begin(), modules.end(), module);
	assert(it != modules.end());
	// Remove the module
	modules.erase(it);
	// Remove id
	module->id = 0;
}

void Engine::resetModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);

	module->reset();
}

void Engine::randomizeModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);

	module->randomize();
}

void Engine::bypassModule(Module *module, bool bypass) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	if (bypass) {
		for (Output &output : module->outputs) {
			// This also zeros all voltages
			output.setChannels(0);
		}
		module->cpuTime = 0.f;
	}
	else {
		// Set all outputs to 1 channel
		for (Output &output : module->outputs) {
			// Don't use Port::setChannels() so we maintain all previous voltages
			output.channels = 1;
		}
	}
	module->bypass = bypass;
}

static void Engine_updateConnected(Engine *engine) {
	// Set everything to unconnected
	for (Module *module : engine->modules) {
		for (Input &input : module->inputs) {
			input.active = false;
		}
		for (Output &output : module->outputs) {
			output.active = false;
		}
	}
	// Set inputs/outputs to active
	for (Cable *cable : engine->cables) {
		cable->outputModule->outputs[cable->outputId].active = true;
		cable->inputModule->inputs[cable->inputId].active = true;
	}
}

void Engine::addCable(Cable *cable) {
	assert(cable);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// Check cable properties
	assert(cable->outputModule);
	assert(cable->inputModule);
	// Check that the cable is not already added, and that the input is not already used by another cable
	for (Cable *cable2 : cables) {
		assert(cable2 != cable);
		assert(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId));
	}
	// Set ID
	if (cable->id == 0) {
		// Automatically assign ID
		cable->id = internal->nextCableId++;
	}
	else {
		// Manual ID
		assert(cable->id < internal->nextCableId);
		// Check that the ID is not already taken
		for (Cable *w : cables) {
			assert(cable->id != w->id);
		}
	}
	// Add the cable
	cables.push_back(cable);
	Engine_updateConnected(this);
}

void Engine::removeCable(Cable *cable) {
	assert(cable);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// Check that the cable is already added
	auto it = std::find(cables.begin(), cables.end(), cable);
	assert(it != cables.end());
	// Set input to inactive
	Input &input = cable->inputModule->inputs[cable->inputId];
	input.setChannels(0);
	// Remove the cable
	cables.erase(it);
	Engine_updateConnected(this);
	// Remove ID
	cable->id = 0;
}

void Engine::setParam(Module *module, int paramId, float value) {
	// TODO Does this need to be thread-safe?
	module->params[paramId].value = value;
}

float Engine::getParam(Module *module, int paramId) {
	return module->params[paramId].value;
}

void Engine::setSmoothParam(Module *module, int paramId, float value) {
	// If another param is being smoothed, jump value
	if (internal->smoothModule && !(internal->smoothModule == module && internal->smoothParamId == paramId)) {
		internal->smoothModule->params[internal->smoothParamId].value = internal->smoothValue;
	}
	internal->smoothParamId = paramId;
	internal->smoothValue = value;
	internal->smoothModule = module;
}

float Engine::getSmoothParam(Module *module, int paramId) {
	if (internal->smoothModule == module && internal->smoothParamId == paramId)
		return internal->smoothValue;
	return getParam(module, paramId);
}

int Engine::getNextModuleId() {
	return internal->nextModuleId++;
}

void Engine::setSampleRate(float newSampleRate) {
	internal->sampleRateRequested = newSampleRate;
}

float Engine::getSampleRate() {
	return internal->sampleRate;
}

float Engine::getSampleTime() {
	return internal->sampleTime;
}

} // namespace rack
<commit_msg>Use guided scheduling mode in openmp for. Seems to be 5% better in my tests.<commit_after>#include "engine/Engine.hpp"
#include "settings.hpp"
#include "system.hpp"

#include <algorithm>
#include <chrono>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <omp.h>
#include <xmmintrin.h>
#include <pmmintrin.h>

namespace rack {

/** Threads which obtain a VIPLock will cause wait() to block for other less important threads.
This does not provide the VIPs with an exclusive lock. That should be left up to another mutex shared between the less important thread.
*/
struct VIPMutex {
	int count = 0;
	std::condition_variable cv;
	std::mutex countMutex;

	/** Blocks until there are no remaining VIPLocks */
	void wait() {
		std::unique_lock<std::mutex> lock(countMutex);
		while (count > 0)
			cv.wait(lock);
	}
};

struct VIPLock {
	VIPMutex &m;
	VIPLock(VIPMutex &m) : m(m) {
		std::unique_lock<std::mutex> lock(m.countMutex);
		m.count++;
	}
	~VIPLock() {
		std::unique_lock<std::mutex> lock(m.countMutex);
		m.count--;
		lock.unlock();
		m.cv.notify_all();
	}
};

struct Engine::Internal {
	bool running = false;
	float sampleRate;
	float sampleTime;
	float sampleRateRequested;

	int nextModuleId = 1;
	int nextCableId = 1;

	// Parameter smoothing
	Module *smoothModule = NULL;
	int smoothParamId;
	float smoothValue;

	std::mutex mutex;
	std::thread thread;
	VIPMutex vipMutex;
};

Engine::Engine() {
	internal = new Internal;

	float sampleRate = 44100.f;
	internal->sampleRate = sampleRate;
	internal->sampleTime = 1 / sampleRate;
	internal->sampleRateRequested = sampleRate;

	threadCount = 1;
}

Engine::~Engine() {
	// Make sure there are no cables or modules in the rack on destruction. This suggests that a module failed to remove itself before the RackWidget was destroyed.
	assert(cables.empty());
	assert(modules.empty());

	delete internal;
}

static void Engine_step(Engine *engine) {
	// Sample rate
	if (engine->internal->sampleRateRequested != engine->internal->sampleRate) {
		engine->internal->sampleRate = engine->internal->sampleRateRequested;
		engine->internal->sampleTime = 1 / engine->internal->sampleRate;
		for (Module *module : engine->modules) {
			module->onSampleRateChange();
		}
	}

	// Param smoothing
	{
		Module *smoothModule = engine->internal->smoothModule;
		int smoothParamId = engine->internal->smoothParamId;
		float smoothValue = engine->internal->smoothValue;
		if (smoothModule) {
			Param *param = &smoothModule->params[smoothParamId];
			float value = param->value;
			// decay rate is 1 graphics frame
			const float smoothLambda = 60.f;
			float newValue = value + (smoothValue - value) * smoothLambda * engine->internal->sampleTime;
			if (value == newValue || !(param->minValue <= newValue && newValue <= param->maxValue)) {
				// Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats), or if newValue is out of bounds
				param->setValue(smoothValue);
				engine->internal->smoothModule = NULL;
			}
			else {
				param->value = newValue;
			}
		}
	}

	float cpuLambda = engine->internal->sampleTime / 2.f;

	// Iterate modules
	int modulesLen = engine->modules.size();
	#pragma omp parallel for num_threads(engine->threadCount) schedule(guided, 1)
	for (int i = 0; i < modulesLen; i++) {
		Module *module = engine->modules[i];
		if (!module->bypass) {
			// Step module
			if (settings::powerMeter) {
				auto startTime = std::chrono::high_resolution_clock::now();

				module->step();

				auto stopTime = std::chrono::high_resolution_clock::now();
				float cpuTime = std::chrono::duration<float>(stopTime - startTime).count();
				// Smooth cpu time
				module->cpuTime += (cpuTime - module->cpuTime) * cpuLambda;
			}
			else {
				module->step();
			}
		}

		// Iterate ports and step plug lights
		for (Input &input : module->inputs) {
			input.step();
		}
		for (Output &output : module->outputs) {
			output.step();
		}
	}

	// Step cables
	for (Cable *cable : engine->cables) {
		cable->step();
	}
}

static void Engine_run(Engine *engine) {
#if defined ARCH_LIN
	// Name thread
	system::setThreadName("Engine");
#endif
	// Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
	// https://software.intel.com/en-us/node/682949
	_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
	_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);

	// Every time the engine waits and locks a mutex, it steps this many frames
	const int mutexSteps = 64;
	// Time in seconds that the engine is rushing ahead of the estimated clock time
	double ahead = 0.0;
	auto lastTime = std::chrono::high_resolution_clock::now();

	while (engine->internal->running) {
		engine->internal->vipMutex.wait();

		if (!engine->paused) {
			std::lock_guard<std::mutex> lock(engine->internal->mutex);
			// auto startTime = std::chrono::high_resolution_clock::now();

			for (int i = 0; i < mutexSteps; i++) {
				Engine_step(engine);
			}

			// auto stopTime = std::chrono::high_resolution_clock::now();
			// float cpuTime = std::chrono::duration<float>(stopTime - startTime).count();
			// DEBUG("%g", cpuTime / mutexSteps * 44100);
		}

		double stepTime = mutexSteps * engine->internal->sampleTime;
		ahead += stepTime;
		auto currTime = std::chrono::high_resolution_clock::now();
		const double aheadFactor = 2.0;
		ahead -= aheadFactor * std::chrono::duration<double>(currTime - lastTime).count();
		lastTime = currTime;
		ahead = std::fmax(ahead, 0.0);

		// Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate
		// The number of steps to wait before possibly sleeping
		const double aheadMax = 1.0; // seconds
		if (ahead > aheadMax) {
			std::this_thread::sleep_for(std::chrono::duration<double>(stepTime));
		}
	}
}

void Engine::start() {
	internal->running = true;
	internal->thread = std::thread(Engine_run, this);
}

void Engine::stop() {
	internal->running = false;
	internal->thread.join();
}

void Engine::addModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// Check that the module is not already added
	auto it = std::find(modules.begin(), modules.end(), module);
	assert(it == modules.end());
	// Set ID
	if (module->id == 0) {
		// Automatically assign ID
		module->id = internal->nextModuleId++;
	}
	else {
		// Manual ID
		assert(module->id < internal->nextModuleId);
		// Check that the ID is not already taken
		for (Module *m : modules) {
			assert(module->id != m->id);
		}
	}
	// Add module
	modules.push_back(module);
}

void Engine::removeModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// If a param is being smoothed on this module, stop smoothing it immediately
	if (module == internal->smoothModule) {
		internal->smoothModule = NULL;
	}
	// Check that all cables are disconnected
	for (Cable *cable : cables) {
		assert(cable->outputModule != module);
		assert(cable->inputModule != module);
	}
	// Check that the module actually exists
	auto it = std::find(modules.begin(), modules.end(), module);
	assert(it != modules.end());
	// Remove the module
	modules.erase(it);
	// Remove id
	module->id = 0;
}

void Engine::resetModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);

	module->reset();
}

void Engine::randomizeModule(Module *module) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);

	module->randomize();
}

void Engine::bypassModule(Module *module, bool bypass) {
	assert(module);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	if (bypass) {
		for (Output &output : module->outputs) {
			// This also zeros all voltages
			output.setChannels(0);
		}
		module->cpuTime = 0.f;
	}
	else {
		// Set all outputs to 1 channel
		for (Output &output : module->outputs) {
			// Don't use Port::setChannels() so we maintain all previous voltages
			output.channels = 1;
		}
	}
	module->bypass = bypass;
}

static void Engine_updateConnected(Engine *engine) {
	// Set everything to unconnected
	for (Module *module : engine->modules) {
		for (Input &input : module->inputs) {
			input.active = false;
		}
		for (Output &output : module->outputs) {
			output.active = false;
		}
	}
	// Set inputs/outputs to active
	for (Cable *cable : engine->cables) {
		cable->outputModule->outputs[cable->outputId].active = true;
		cable->inputModule->inputs[cable->inputId].active = true;
	}
}

void Engine::addCable(Cable *cable) {
	assert(cable);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// Check cable properties
	assert(cable->outputModule);
	assert(cable->inputModule);
	// Check that the cable is not already added, and that the input is not already used by another cable
	for (Cable *cable2 : cables) {
		assert(cable2 != cable);
		assert(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId));
	}
	// Set ID
	if (cable->id == 0) {
		// Automatically assign ID
		cable->id = internal->nextCableId++;
	}
	else {
		// Manual ID
		assert(cable->id < internal->nextCableId);
		// Check that the ID is not already taken
		for (Cable *w : cables) {
			assert(cable->id != w->id);
		}
	}
	// Add the cable
	cables.push_back(cable);
	Engine_updateConnected(this);
}

void Engine::removeCable(Cable *cable) {
	assert(cable);
	VIPLock vipLock(internal->vipMutex);
	std::lock_guard<std::mutex> lock(internal->mutex);
	// Check that the cable is already added
	auto it = std::find(cables.begin(), cables.end(), cable);
	assert(it != cables.end());
	// Set input to inactive
	Input &input = cable->inputModule->inputs[cable->inputId];
	input.setChannels(0);
	// Remove the cable
	cables.erase(it);
	Engine_updateConnected(this);
	// Remove ID
	cable->id = 0;
}

void Engine::setParam(Module *module, int paramId, float value) {
	// TODO Does this need to be thread-safe?
	module->params[paramId].value = value;
}

float Engine::getParam(Module *module, int paramId) {
	return module->params[paramId].value;
}

void Engine::setSmoothParam(Module *module, int paramId, float value) {
	// If another param is being smoothed, jump value
	if (internal->smoothModule && !(internal->smoothModule == module && internal->smoothParamId == paramId)) {
		internal->smoothModule->params[internal->smoothParamId].value = internal->smoothValue;
	}
	internal->smoothParamId = paramId;
	internal->smoothValue = value;
	internal->smoothModule = module;
}

float Engine::getSmoothParam(Module *module, int paramId) {
	if (internal->smoothModule == module && internal->smoothParamId == paramId)
		return internal->smoothValue;
	return getParam(module, paramId);
}

int Engine::getNextModuleId() {
	return internal->nextModuleId++;
}

void Engine::setSampleRate(float newSampleRate) {
	internal->sampleRateRequested = newSampleRate;
}

float Engine::getSampleRate() {
	return internal->sampleRate;
}

float Engine::getSampleTime() {
	return internal->sampleTime;
}

} // namespace rack
<|endoftext|>


<commit_before>#include <stdio.h>

#include "engine/engine.hpp"

namespace qrw
{
	Engine::Engine()
	:	board(0),
		status(EES_UNDEFINED)
	{
		players[0].setName("Player The First");
		players[0].setId(0);
		players[1].setName("Player The Second");
		players[1].setId(1);
	}

	Engine::~Engine()
	{}

	void Engine::init(int boardwidth, int boardheight)
	{
		delete board;
		board = new Board(boardwidth, boardheight);
		currentplayer = 0;
		status = EES_PREPARE;

		int maxarmysize = getMaxPlayerUnits();
		players[0].getArmy().deleteAllUnits();
		players[0].getArmy().setMaxSize(maxarmysize);
		players[1].getArmy().deleteAllUnits();
		players[1].getArmy().setMaxSize(maxarmysize);
	}

	void Engine::startGame()
	{
		currentplayer = 0;
		status = EES_RUNNING;
	}

	ENGINSTATES Engine::getStatus()
	{
		return status;
	}

	int Engine::getMaxPlayerUnits()
	{
		if(board)
			return (board->getHeight() * board->getWidth()) / 3;
		return 0;
	}

	void Engine::createPlayerUnits(int playerid, std::map<UNITTYPES, int> unitcounts)
	{
		Player* player = getPlayer(playerid);
		UNITTYPES unittype;
		Unit* unit;

		for(auto iter = unitcounts.begin(); iter != unitcounts.end(); ++iter)
		{
			unittype = iter->first;
			for(int i = 0; i < iter->second; ++i)
			{
				// Create new unit
				switch(unittype)
				{
					case EUT_SWORDMAN:
						unit = new Unit(EUT_SWORDMAN, 5, 2, 1, 1, 3, player);
						break;

					case EUT_ARCHER:
						unit = new Unit(EUT_ARCHER, 5, 2, 1, 3, 2, player);
						break;

					default:
						unit = new Unit(EUT_SPEARMAN, 5, 2, 1, 2, 2, player);
						break;
				}
				// Add new unit to army
				player->getArmy().addUnit(unit);
			}
		}
	}

	// bool Engine::setUnits(int playeroneunits[EUT_NUMBEROFUNITTYPES],
		// int playertwounits[EUT_NUMBEROFUNITTYPES])
	// {
	//	players[0].clearUnits();
	//	players[1].clearUnits();

	//	if(status != EES_PREPARE)
	//		return false;
	//	setPlayerUnits(0, playeroneunits);
	//	setPlayerUnits(1, playertwounits);
	//	return true;
	// }

	// void Engine::setPlayerUnits(int id, int unitnumbers[EUT_NUMBEROFUNITTYPES])
	// {
	//	Player* player = getPlayer(id);
	//	int i;
	//	// Attention! player is "overwritten" in the method Player::addUnit(...).
	//	for(i = 0; i < unitnumbers[EUT_SWORDMAN]; ++i)
	//		player->addUnit(new Unit(EUT_SWORDMAN, 2, 1, 1, 3, player));
	//	for(i = 0; i < unitnumbers[EUT_ARCHER]; ++i)
	//		player->addUnit(new Unit(EUT_ARCHER, 2, 1, 3, 2, player));
	//	for(i = 0; i < unitnumbers[EUT_SPEARMAN]; ++i)
	//		player->addUnit(new Unit(EUT_SPEARMAN, 2, 1, 2, 2, player));
	// }

	Board* Engine::getBoard()
	{
		return board;
	}

	Player& Engine::getCurrentPlayer()
	{
		return players[currentplayer];
	}

	void Engine::endTurn()
	{
		// Reset movement of current players units.
		Army& army = getCurrentPlayer().getArmy();

		for(int i = 0; i < EUT_NUMBEROFUNITTYPES; ++i)
		{
			std::set<Unit*>& unitset = army.getUnitsByType((UNITTYPES)i);

			for(auto iter = unitset.begin(); iter != unitset.end(); ++iter)
				(*iter)->setCurrentMovement((*iter)->getMovement());
		}

		currentplayer = (currentplayer + 1) % 2;
	}

	/**
	 * @Return: 0 - success, -1 - wrong player, -2 origin empty,
	 *			-3 on destination is unit of same player, -4 origin out of range,
	 *			-5 dest out of ranage, -6 not enough movement,
	 *			-7 game not running, -8 unit on origin died, -9 enemy unit
	 *			was not defeated, -10 enemy out of range, -11 defender died
	 */
	int Engine::moveUnitIngame(int orx, int ory, int destx, int desty)
	{
		// Game is not running
		if(status != EES_RUNNING)
			return -7;

		Square* orsquare = board->getSquare(orx, ory);
		// index out of range
		if(orsquare == 0)
			return -4;
		// no unit on this square
		if(orsquare->getUnit() == 0)
			return -2;

		Square* destsquare = board->getSquare(destx, desty);
		// index out of range
		if(destsquare == 0)
			return -5;

		Unit* srcunit = orsquare->getUnit();
				// Unit does not belong to current player
		if(srcunit->getPlayer() != &getCurrentPlayer())
			return -1;

		int distance = orsquare->getDistance(destsquare);
		// Distance is too far
		if(distance > srcunit->getCurrentMovement())
			return -6;

		// Is there a unit on the destination?
		Unit* destunit = destsquare->getUnit();
		if(destunit != 0)
		{
			// unit on destination belongs to same player
			if(destunit->getPlayer() == srcunit->getPlayer())
				return -3;
			// otherwise: battle

			// Check for range
			printf("distance: %d", distance);
			if(distance > srcunit->getRange())
				return -10;

			// get modificators
			int attackmods[] = {0, 0};
			int defensemods[] = {0, 0};
			if(orsquare->getTerrain() != NULL)
			{
				attackmods[0] = orsquare->getTerrain()->getModificators()[0];
				attackmods[1] = orsquare->getTerrain()->getModificators()[1];
			}
			if(destsquare->getTerrain() != NULL)
			{
				defensemods[0] = destsquare->getTerrain()->getModificators()[0];
				defensemods[1] = destsquare->getTerrain()->getModificators()[1];
			}

			srcunit->attack(destunit, attackmods, defensemods);
			srcunit->setCurrentMovement(0);
			// Attacker died
			if(srcunit->getHP() == 0)
			{
				orsquare->setUnit(0);
				return -8;
			}
			// No one died
			else if(destunit->getHP() > 0)
			{
				return -9;
			}
			// Defender died
			else
			{
				orsquare->setUnit(0);
				destsquare->setUnit(srcunit);
				return -11;
			}
		}

		srcunit->setCurrentMovement(srcunit->getCurrentMovement() - distance);
		orsquare->setUnit(0);
		destsquare->setUnit(srcunit);
		return 0;
	}

	int Engine::moveUnitDeployment(int orx, int ory, int destx, int desty)
	{
		Square* orsquare = board->getSquare(orx, ory);
		if(orsquare->getUnit() == NULL)
			return -1;

		Square* destsquare = board->getSquare(destx, desty);

		if(destsquare->getUnit() != NULL)
			return -1;

		destsquare->setUnit(orsquare->getUnit());
		orsquare->setUnit(NULL);
		return 0;
	}

	bool Engine::placeUnit(int x, int y, int playerid, UNITTYPES unittype)
	{
		if(status != EES_PREPARE)
			return false;
		Square* square = board->getSquare(x, y);

		if(square == 0)
			return false;

		if(square->getUnit() != 0)
			return false;

		Player* player = getPlayer(playerid);
		Unit* unit = *(player->getArmy().getUndeployedUnitsByType(unittype).begin());
		if(unit)
		{
			player->getArmy().markUnitAsDeployed(unit);
			square->setUnit(unit);
			return true;
		}
		return false;
	}

	bool Engine::placeTerrain(int x, int y, TERRAINTYPES terraintype)
	{
		if(status != EES_PREPARE)
			return false;
		Square* square = board->getSquare(x, y);
		if(square == NULL)
			return false;

		if(square->getTerrain() != NULL)
			delete square->getTerrain();
		Terrain* terrain;
		switch(terraintype)
		{
			case ET_WOOD:	terrain = new Terrain(ET_WOOD, -1, 1);
							break;
			case ET_HILL:	terrain = new Terrain(ET_HILL, 1, -1);
							break;
			default:		terrain = new Terrain(ET_WALL, 1, 1);
							break;
		}
		square->setTerrain(terrain);
		return true;
	}

	bool Engine::removeTerrain(int x, int y)
	{
		if(status != EES_PREPARE)
			return false;

		Square* square = board->getSquare(x, y);
		if(square == NULL)
			return false;

		if(square->getTerrain())
			delete square->getTerrain();
		square->setTerrain(NULL);
	}

	Player* Engine::getPlayer(int id)
	{
		if(id == 0 || id == 1)
			return &players[id];
		return 0;
	}
}
<commit_msg>Engine sets max unit count for army to INT_MAX.<commit_after>#include <stdio.h>
#include <climits>

#include "engine/engine.hpp"

namespace qrw
{
	Engine::Engine()
	:	board(0),
		status(EES_UNDEFINED)
	{
		players[0].setName("Player The First");
		players[0].setId(0);
		players[1].setName("Player The Second");
		players[1].setId(1);
	}

	Engine::~Engine()
	{}

	void Engine::init(int boardwidth, int boardheight)
	{
		delete board;
		board = new Board(boardwidth, boardheight);
		currentplayer = 0;
		status = EES_PREPARE;

		int maxarmysize = INT_MAX;
		// int maxarmysize = getMaxPlayerUnits();
		players[0].getArmy().deleteAllUnits();
		players[0].getArmy().setMaxSize(maxarmysize);
		players[1].getArmy().deleteAllUnits();
		players[1].getArmy().setMaxSize(maxarmysize);
	}

	void Engine::startGame()
	{
		currentplayer = 0;
		status = EES_RUNNING;
	}

	ENGINSTATES Engine::getStatus()
	{
		return status;
	}

	int Engine::getMaxPlayerUnits()
	{
		if(board)
			return (board->getHeight() * board->getWidth()) / 3;
		return 0;
	}

	void Engine::createPlayerUnits(int playerid, std::map<UNITTYPES, int> unitcounts)
	{
		Player* player = getPlayer(playerid);
		UNITTYPES unittype;
		Unit* unit;

		for(auto iter = unitcounts.begin(); iter != unitcounts.end(); ++iter)
		{
			unittype = iter->first;
			for(int i = 0; i < iter->second; ++i)
			{
				// Create new unit
				switch(unittype)
				{
					case EUT_SWORDMAN:
						unit = new Unit(EUT_SWORDMAN, 5, 2, 1, 1, 3, player);
						break;

					case EUT_ARCHER:
						unit = new Unit(EUT_ARCHER, 5, 2, 1, 3, 2, player);
						break;

					default:
						unit = new Unit(EUT_SPEARMAN, 5, 2, 1, 2, 2, player);
						break;
				}
				// Add new unit to army
				player->getArmy().addUnit(unit);
			}
		}
	}

	// bool Engine::setUnits(int playeroneunits[EUT_NUMBEROFUNITTYPES],
		// int playertwounits[EUT_NUMBEROFUNITTYPES])
	// {
	//	players[0].clearUnits();
	//	players[1].clearUnits();

	//	if(status != EES_PREPARE)
	//		return false;
	//	setPlayerUnits(0, playeroneunits);
	//	setPlayerUnits(1, playertwounits);
	//	return true;
	// }

	// void Engine::setPlayerUnits(int id, int unitnumbers[EUT_NUMBEROFUNITTYPES])
	// {
	//	Player* player = getPlayer(id);
	//	int i;
	//	// Attention! player is "overwritten" in the method Player::addUnit(...).
	//	for(i = 0; i < unitnumbers[EUT_SWORDMAN]; ++i)
	//		player->addUnit(new Unit(EUT_SWORDMAN, 2, 1, 1, 3, player));
	//	for(i = 0; i < unitnumbers[EUT_ARCHER]; ++i)
	//		player->addUnit(new Unit(EUT_ARCHER, 2, 1, 3, 2, player));
	//	for(i = 0; i < unitnumbers[EUT_SPEARMAN]; ++i)
	//		player->addUnit(new Unit(EUT_SPEARMAN, 2, 1, 2, 2, player));
	// }

	Board* Engine::getBoard()
	{
		return board;
	}

	Player& Engine::getCurrentPlayer()
	{
		return players[currentplayer];
	}

	void Engine::endTurn()
	{
		// Reset movement of current players units.
		Army& army = getCurrentPlayer().getArmy();

		for(int i = 0; i < EUT_NUMBEROFUNITTYPES; ++i)
		{
			std::set<Unit*>& unitset = army.getUnitsByType((UNITTYPES)i);

			for(auto iter = unitset.begin(); iter != unitset.end(); ++iter)
				(*iter)->setCurrentMovement((*iter)->getMovement());
		}

		currentplayer = (currentplayer + 1) % 2;
	}

	/**
	 * @Return: 0 - success, -1 - wrong player, -2 origin empty,
	 *			-3 on destination is unit of same player, -4 origin out of range,
	 *			-5 dest out of ranage, -6 not enough movement,
	 *			-7 game not running, -8 unit on origin died, -9 enemy unit
	 *			was not defeated, -10 enemy out of range, -11 defender died
	 */
	int Engine::moveUnitIngame(int orx, int ory, int destx, int desty)
	{
		// Game is not running
		if(status != EES_RUNNING)
			return -7;

		Square* orsquare = board->getSquare(orx, ory);
		// index out of range
		if(orsquare == 0)
			return -4;
		// no unit on this square
		if(orsquare->getUnit() == 0)
			return -2;

		Square* destsquare = board->getSquare(destx, desty);
		// index out of range
		if(destsquare == 0)
			return -5;

		Unit* srcunit = orsquare->getUnit();
				// Unit does not belong to current player
		if(srcunit->getPlayer() != &getCurrentPlayer())
			return -1;

		int distance = orsquare->getDistance(destsquare);
		// Distance is too far
		if(distance > srcunit->getCurrentMovement())
			return -6;

		// Is there a unit on the destination?
		Unit* destunit = destsquare->getUnit();
		if(destunit != 0)
		{
			// unit on destination belongs to same player
			if(destunit->getPlayer() == srcunit->getPlayer())
				return -3;
			// otherwise: battle

			// Check for range
			printf("distance: %d", distance);
			if(distance > srcunit->getRange())
				return -10;

			// get modificators
			int attackmods[] = {0, 0};
			int defensemods[] = {0, 0};
			if(orsquare->getTerrain() != NULL)
			{
				attackmods[0] = orsquare->getTerrain()->getModificators()[0];
				attackmods[1] = orsquare->getTerrain()->getModificators()[1];
			}
			if(destsquare->getTerrain() != NULL)
			{
				defensemods[0] = destsquare->getTerrain()->getModificators()[0];
				defensemods[1] = destsquare->getTerrain()->getModificators()[1];
			}

			srcunit->attack(destunit, attackmods, defensemods);
			srcunit->setCurrentMovement(0);
			// Attacker died
			if(srcunit->getHP() == 0)
			{
				orsquare->setUnit(0);
				return -8;
			}
			// No one died
			else if(destunit->getHP() > 0)
			{
				return -9;
			}
			// Defender died
			else
			{
				orsquare->setUnit(0);
				destsquare->setUnit(srcunit);
				return -11;
			}
		}

		srcunit->setCurrentMovement(srcunit->getCurrentMovement() - distance);
		orsquare->setUnit(0);
		destsquare->setUnit(srcunit);
		return 0;
	}

	int Engine::moveUnitDeployment(int orx, int ory, int destx, int desty)
	{
		Square* orsquare = board->getSquare(orx, ory);
		if(orsquare->getUnit() == NULL)
			return -1;

		Square* destsquare = board->getSquare(destx, desty);

		if(destsquare->getUnit() != NULL)
			return -1;

		destsquare->setUnit(orsquare->getUnit());
		orsquare->setUnit(NULL);
		return 0;
	}

	bool Engine::placeUnit(int x, int y, int playerid, UNITTYPES unittype)
	{
		if(status != EES_PREPARE)
			return false;
		Square* square = board->getSquare(x, y);

		if(square == 0)
			return false;

		if(square->getUnit() != 0)
			return false;

		Player* player = getPlayer(playerid);
		Unit* unit = *(player->getArmy().getUndeployedUnitsByType(unittype).begin());
		if(unit)
		{
			player->getArmy().markUnitAsDeployed(unit);
			square->setUnit(unit);
			return true;
		}
		return false;
	}

	bool Engine::placeTerrain(int x, int y, TERRAINTYPES terraintype)
	{
		if(status != EES_PREPARE)
			return false;
		Square* square = board->getSquare(x, y);
		if(square == NULL)
			return false;

		if(square->getTerrain() != NULL)
			delete square->getTerrain();
		Terrain* terrain;
		switch(terraintype)
		{
			case ET_WOOD:	terrain = new Terrain(ET_WOOD, -1, 1);
							break;
			case ET_HILL:	terrain = new Terrain(ET_HILL, 1, -1);
							break;
			default:		terrain = new Terrain(ET_WALL, 1, 1);
							break;
		}
		square->setTerrain(terrain);
		return true;
	}

	bool Engine::removeTerrain(int x, int y)
	{
		if(status != EES_PREPARE)
			return false;

		Square* square = board->getSquare(x, y);
		if(square == NULL)
			return false;

		if(square->getTerrain())
			delete square->getTerrain();
		square->setTerrain(NULL);
	}

	Player* Engine::getPlayer(int id)
	{
		if(id == 0 || id == 1)
			return &players[id];
		return 0;
	}
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

// START SNIPPET: demo

#include <activemq/concurrent/Thread.h>
#include <activemq/concurrent/Runnable.h>
#include <activemq/concurrent/CountDownLatch.h>
#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/util/Integer.h>
#include <activemq/util/Config.h>
#include <activemq/util/Date.h>
#include <cms/Connection.h>
#include <cms/Session.h>
#include <cms/TextMessage.h>
#include <cms/BytesMessage.h>
#include <cms/MapMessage.h>
#include <cms/ExceptionListener.h>
#include <cms/MessageListener.h>
#include <stdlib.h>
#include <iostream>

using namespace activemq::core;
using namespace activemq::util;
using namespace activemq::concurrent;
using namespace cms;
using namespace std;

class HelloWorldProducer : public Runnable {
private:

    Connection* connection;
    Session* session;
    Destination* destination;
    MessageProducer* producer;
    int numMessages;
    bool useTopic;
    std::string brokerURI;

public:

    HelloWorldProducer( const std::string& brokerURI,
                        int numMessages, bool useTopic = false ){
        connection = NULL;
        session = NULL;
        destination = NULL;
        producer = NULL;
        this->numMessages = numMessages;
        this->useTopic = useTopic;
        this->brokerURI = brokerURI;
    }

    virtual ~HelloWorldProducer(){
        cleanup();
    }

    virtual void run() {
        try {
            // Create a ConnectionFactory
            ActiveMQConnectionFactory* connectionFactory =
                new ActiveMQConnectionFactory( brokerURI );

            // Create a Connection
            connection = connectionFactory->createConnection();
            connection->start();

            // free the factory, we are done with it.
            delete connectionFactory;

            // Create a Session
            session = connection->createSession( Session::AUTO_ACKNOWLEDGE );

            // Create the destination (Topic or Queue)
            if( useTopic ) {
                destination = session->createTopic( "TEST.FOO" );
            } else {
                destination = session->createQueue( "TEST.FOO" );
            }

            // Create a MessageProducer from the Session to the Topic or Queue
            producer = session->createProducer( destination );
            producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

            // Create the Thread Id String
            string threadIdStr = Integer::toString( Thread::getId() );

            // Create a messages
            string text = (string)"Hello world! from thread " + threadIdStr;

            for( int ix=0; ix<numMessages; ++ix ){
                TextMessage* message = session->createTextMessage( text );

                message->setIntProperty( "Integer", ix );

                // Tell the producer to send the message
                printf( "Sent message #%d from thread %s\n", ix+1, threadIdStr.c_str() );
                producer->send( message );

                delete message;
            }

        }catch ( CMSException& e ) {
            e.printStackTrace();
        }
    }

private:

    void cleanup(){

            // Destroy resources.
            try{
                if( destination != NULL ) delete destination;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            destination = NULL;

            try{
                if( producer != NULL ) delete producer;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            producer = NULL;

            // Close open resources.
            try{
                if( session != NULL ) session->close();
                if( connection != NULL ) connection->close();
            }catch ( CMSException& e ) { e.printStackTrace(); }

            try{
                if( session != NULL ) delete session;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            session = NULL;

            try{
                if( connection != NULL ) delete connection;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            connection = NULL;
    }
};

class HelloWorldConsumer : public ExceptionListener,
                           public MessageListener,
                           public Runnable {

private:

    CountDownLatch latch;
    CountDownLatch doneLatch;
    Connection* connection;
    Session* session;
    Destination* destination;
    MessageConsumer* consumer;
    long waitMillis;
    bool useTopic;
    std::string brokerURI;

public:

    HelloWorldConsumer( const std::string& brokerURI,
                        long numMessages,
                        bool useTopic = false,
                        long waitMillis = 30000 )
                         : latch(1), doneLatch(numMessages){
        connection = NULL;
        session = NULL;
        destination = NULL;
        consumer = NULL;
        this->waitMillis = waitMillis;
        this->useTopic = useTopic;
        this->brokerURI = brokerURI;
    }
    virtual ~HelloWorldConsumer(){
        cleanup();
    }

    void waitUnitlReady() {
        latch.await();
    }

    virtual void run() {

        try {

            // Create a ConnectionFactory
            ActiveMQConnectionFactory* connectionFactory =
                new ActiveMQConnectionFactory( brokerURI );

            // Create a Connection
            connection = connectionFactory->createConnection();
            delete connectionFactory;
            connection->start();

            connection->setExceptionListener(this);

            // Create a Session
            session = connection->createSession( Session::AUTO_ACKNOWLEDGE );

            // Create the destination (Topic or Queue)
            if( useTopic ) {
                destination = session->createTopic( "TEST.FOO" );
            } else {
                destination = session->createQueue( "TEST.FOO" );
            }

            // Create a MessageConsumer from the Session to the Topic or Queue
            consumer = session->createConsumer( destination );

            consumer->setMessageListener( this );

            std::cout.flush();
            std::cerr.flush();

            // Indicate we are ready for messages.
            latch.countDown();

            // Wait while asynchronous messages come in.
            doneLatch.await( waitMillis );

        } catch (CMSException& e) {
            e.printStackTrace();
        }
    }

    // Called from the consumer since this class is a registered MessageListener.
    virtual void onMessage( const Message* message ){

        static int count = 0;

        try
        {
            count++;
            const TextMessage* textMessage =
                dynamic_cast< const TextMessage* >( message );
            string text = "";

            if( textMessage != NULL ) {
                text = textMessage->getText();
            } else {
                text = "NOT A TEXTMESSAGE!";
            }

            printf( "Message #%d Received: %s\n", count, text.c_str() );
        } catch (CMSException& e) {
            e.printStackTrace();
        }

        // No matter what, tag the count down latch until done.
        doneLatch.countDown();
    }

    // If something bad happens you see it here as this class is also been
    // registered as an ExceptionListener with the connection.
    virtual void onException( const CMSException& ex AMQCPP_UNUSED) {
        printf("JMS Exception occured.  Shutting down client.\n");
    }

private:

    void cleanup(){

        //*************************************************
        // Always close destination, consumers and producers before
        // you destroy their sessions and connection.
        //*************************************************

        // Destroy resources.
        try{
            if( destination != NULL ) delete destination;
        }catch (CMSException& e) { e.printStackTrace(); }
        destination = NULL;

        try{
            if( consumer != NULL ) delete consumer;
        }catch (CMSException& e) { e.printStackTrace(); }
        consumer = NULL;

        // Close open resources.
        try{
            if( session != NULL ) session->close();
            if( connection != NULL ) connection->close();
        }catch (CMSException& e) { e.printStackTrace(); }

        // Now Destroy them
        try{
            if( session != NULL ) delete session;
        }catch (CMSException& e) { e.printStackTrace(); }
        session = NULL;

        try{
            if( connection != NULL ) delete connection;
        }catch (CMSException& e) { e.printStackTrace(); }
        connection = NULL;
    }
};

int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) {

    std::cout << "=====================================================\n";
    std::cout << "Starting the example:" << std::endl;
    std::cout << "-----------------------------------------------------\n";

    // Set the URI to point to the IPAddress of your broker.
    // add any optional params to the url to enable things like
    // tightMarshalling or tcp logging etc.  See the CMS website for
    // a full list of configuration options.
    //
    //  http://activemq.apache.org/cms/
    //
    // Wire Foormat Options:
    // =====================
    // Use either stomp or openwire, the default ports are different for each
    //
    // Examples:
    //    tcp://127.0.0.1:61616                      default to openwire
    //    tcp://127.0.0.1:61616?wireFormat=openwire  same as above
    //    tcp://127.0.0.1:61613?wireFormat=stomp     use stomp instead
    //
    std::string brokerURI =
        "tcp://127.0.0.1:61616"
        "?wireFormat=openwire"
        "&transport.useAsyncSend=true";
//        "&transport.commandTracingEnabled=true"
//        "&transport.tcpTracingEnabled=true";
//        "&wireFormat.tightEncodingEnabled=true";

    //============================================================
    // set to true to use topics instead of queues
    // Note in the code above that this causes createTopic or
    // createQueue to be used in both consumer an producer.
    //============================================================
    bool useTopics = true;
    int numMessages = 2000;

    long long startTime = Date::getCurrentTimeMilliseconds();

    HelloWorldProducer producer( brokerURI, numMessages, useTopics );
    HelloWorldConsumer consumer( brokerURI, numMessages, useTopics );

    // Start the consumer thread.
    Thread consumerThread( &consumer );
    consumerThread.start();

    // Wait for the consumer to indicate that its ready to go.
    consumer.waitUnitlReady();

    // Start the producer thread.
    Thread producerThread( &producer );
    producerThread.start();

    // Wait for the threads to complete.
    producerThread.join();
    consumerThread.join();

    long long endTime = Date::getCurrentTimeMilliseconds();
    double totalTime = (endTime - startTime) / 1000.0;

    std::cout << "Time to completion = " << totalTime << " seconds." << std::endl;
    std::cout << "-----------------------------------------------------\n";
    std::cout << "Finished with the example." << std::endl;
    std::cout << "=====================================================\n";
}

// END SNIPPET: demo
<commit_msg>Cleaning up the examples<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

// START SNIPPET: demo

#include <activemq/concurrent/Thread.h>
#include <activemq/concurrent/Runnable.h>
#include <activemq/concurrent/CountDownLatch.h>
#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/util/Integer.h>
#include <activemq/util/Config.h>
#include <activemq/util/Date.h>
#include <cms/Connection.h>
#include <cms/Session.h>
#include <cms/TextMessage.h>
#include <cms/BytesMessage.h>
#include <cms/MapMessage.h>
#include <cms/ExceptionListener.h>
#include <cms/MessageListener.h>
#include <stdlib.h>
#include <iostream>

using namespace activemq::core;
using namespace activemq::util;
using namespace activemq::concurrent;
using namespace cms;
using namespace std;

class HelloWorldProducer : public Runnable {
private:

    Connection* connection;
    Session* session;
    Destination* destination;
    MessageProducer* producer;
    int numMessages;
    bool useTopic;
    std::string brokerURI;

public:

    HelloWorldProducer( const std::string& brokerURI,
                        int numMessages, bool useTopic = false ){
        connection = NULL;
        session = NULL;
        destination = NULL;
        producer = NULL;
        this->numMessages = numMessages;
        this->useTopic = useTopic;
        this->brokerURI = brokerURI;
    }

    virtual ~HelloWorldProducer(){
        cleanup();
    }

    virtual void run() {
        try {
            // Create a ConnectionFactory
            ActiveMQConnectionFactory* connectionFactory =
                new ActiveMQConnectionFactory( brokerURI );

            // Create a Connection
            connection = connectionFactory->createConnection();
            connection->start();

            // free the factory, we are done with it.
            delete connectionFactory;

            // Create a Session
            session = connection->createSession( Session::AUTO_ACKNOWLEDGE );

            // Create the destination (Topic or Queue)
            if( useTopic ) {
                destination = session->createTopic( "TEST.FOO" );
            } else {
                destination = session->createQueue( "TEST.FOO" );
            }

            // Create a MessageProducer from the Session to the Topic or Queue
            producer = session->createProducer( destination );
            producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );

            // Create the Thread Id String
            string threadIdStr = Integer::toString( Thread::getId() );

            // Create a messages
            string text = (string)"Hello world! from thread " + threadIdStr;

            for( int ix=0; ix<numMessages; ++ix ){
                TextMessage* message = session->createTextMessage( text );

                message->setIntProperty( "Integer", ix );

                // Tell the producer to send the message
                printf( "Sent message #%d from thread %s\n", ix+1, threadIdStr.c_str() );
                producer->send( message );

                delete message;
            }

        }catch ( CMSException& e ) {
            e.printStackTrace();
        }
    }

private:

    void cleanup(){

            // Destroy resources.
            try{
                if( destination != NULL ) delete destination;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            destination = NULL;

            try{
                if( producer != NULL ) delete producer;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            producer = NULL;

            // Close open resources.
            try{
                if( session != NULL ) session->close();
                if( connection != NULL ) connection->close();
            }catch ( CMSException& e ) { e.printStackTrace(); }

            try{
                if( session != NULL ) delete session;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            session = NULL;

            try{
                if( connection != NULL ) delete connection;
            }catch ( CMSException& e ) { e.printStackTrace(); }
            connection = NULL;
    }
};

class HelloWorldConsumer : public ExceptionListener,
                           public MessageListener,
                           public Runnable {

private:

    CountDownLatch latch;
    CountDownLatch doneLatch;
    Connection* connection;
    Session* session;
    Destination* destination;
    MessageConsumer* consumer;
    long waitMillis;
    bool useTopic;
    std::string brokerURI;

public:

    HelloWorldConsumer( const std::string& brokerURI,
                        long numMessages,
                        bool useTopic = false,
                        long waitMillis = 30000 )
                         : latch(1), doneLatch(numMessages){
        connection = NULL;
        session = NULL;
        destination = NULL;
        consumer = NULL;
        this->waitMillis = waitMillis;
        this->useTopic = useTopic;
        this->brokerURI = brokerURI;
    }
    virtual ~HelloWorldConsumer(){
        cleanup();
    }

    void waitUnitlReady() {
        latch.await();
    }

    virtual void run() {

        try {

            // Create a ConnectionFactory
            ActiveMQConnectionFactory* connectionFactory =
                new ActiveMQConnectionFactory( brokerURI );

            // Create a Connection
            connection = connectionFactory->createConnection();
            delete connectionFactory;
            connection->start();

            connection->setExceptionListener(this);

            // Create a Session
            session = connection->createSession( Session::AUTO_ACKNOWLEDGE );

            // Create the destination (Topic or Queue)
            if( useTopic ) {
                destination = session->createTopic( "TEST.FOO" );
            } else {
                destination = session->createQueue( "TEST.FOO" );
            }

            // Create a MessageConsumer from the Session to the Topic or Queue
            consumer = session->createConsumer( destination );

            consumer->setMessageListener( this );

            std::cout.flush();
            std::cerr.flush();

            // Indicate we are ready for messages.
            latch.countDown();

            // Wait while asynchronous messages come in.
            doneLatch.await( waitMillis );

        } catch (CMSException& e) {
            e.printStackTrace();
        }
    }

    // Called from the consumer since this class is a registered MessageListener.
    virtual void onMessage( const Message* message ){

        static int count = 0;

        try
        {
            count++;
            const TextMessage* textMessage =
                dynamic_cast< const TextMessage* >( message );
            string text = "";

            if( textMessage != NULL ) {
                text = textMessage->getText();
            } else {
                text = "NOT A TEXTMESSAGE!";
            }

            printf( "Message #%d Received: %s\n", count, text.c_str() );
        } catch (CMSException& e) {
            e.printStackTrace();
        }

        // No matter what, tag the count down latch until done.
        doneLatch.countDown();
    }

    // If something bad happens you see it here as this class is also been
    // registered as an ExceptionListener with the connection.
    virtual void onException( const CMSException& ex AMQCPP_UNUSED) {
        printf("CMS Exception occured.  Shutting down client.\n");
    }

private:

    void cleanup(){

        //*************************************************
        // Always close destination, consumers and producers before
        // you destroy their sessions and connection.
        //*************************************************

        // Destroy resources.
        try{
            if( destination != NULL ) delete destination;
        }catch (CMSException& e) { e.printStackTrace(); }
        destination = NULL;

        try{
            if( consumer != NULL ) delete consumer;
        }catch (CMSException& e) { e.printStackTrace(); }
        consumer = NULL;

        // Close open resources.
        try{
            if( session != NULL ) session->close();
            if( connection != NULL ) connection->close();
        }catch (CMSException& e) { e.printStackTrace(); }

        // Now Destroy them
        try{
            if( session != NULL ) delete session;
        }catch (CMSException& e) { e.printStackTrace(); }
        session = NULL;

        try{
            if( connection != NULL ) delete connection;
        }catch (CMSException& e) { e.printStackTrace(); }
        connection = NULL;
    }
};

int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) {

    std::cout << "=====================================================\n";
    std::cout << "Starting the example:" << std::endl;
    std::cout << "-----------------------------------------------------\n";

    // Set the URI to point to the IPAddress of your broker.
    // add any optional params to the url to enable things like
    // tightMarshalling or tcp logging etc.  See the CMS website for
    // a full list of configuration options.
    //
    //  http://activemq.apache.org/cms/
    //
    // Wire Foormat Options:
    // =====================
    // Use either stomp or openwire, the default ports are different for each
    //
    // Examples:
    //    tcp://127.0.0.1:61616                      default to openwire
    //    tcp://127.0.0.1:61616?wireFormat=openwire  same as above
    //    tcp://127.0.0.1:61613?wireFormat=stomp     use stomp instead
    //
    std::string brokerURI =
        "tcp://127.0.0.1:61616"
        "?wireFormat=openwire"
        "&transport.useAsyncSend=true";
//        "&transport.commandTracingEnabled=true"
//        "&transport.tcpTracingEnabled=true";
//        "&wireFormat.tightEncodingEnabled=true";

    //============================================================
    // set to true to use topics instead of queues
    // Note in the code above that this causes createTopic or
    // createQueue to be used in both consumer an producer.
    //============================================================
    bool useTopics = true;
    int numMessages = 2000;

    long long startTime = Date::getCurrentTimeMilliseconds();

    HelloWorldProducer producer( brokerURI, numMessages, useTopics );
    HelloWorldConsumer consumer( brokerURI, numMessages, useTopics );

    // Start the consumer thread.
    Thread consumerThread( &consumer );
    consumerThread.start();

    // Wait for the consumer to indicate that its ready to go.
    consumer.waitUnitlReady();

    // Start the producer thread.
    Thread producerThread( &producer );
    producerThread.start();

    // Wait for the threads to complete.
    producerThread.join();
    consumerThread.join();

    long long endTime = Date::getCurrentTimeMilliseconds();
    double totalTime = (endTime - startTime) / 1000.0;

    std::cout << "Time to completion = " << totalTime << " seconds." << std::endl;
    std::cout << "-----------------------------------------------------\n";
    std::cout << "Finished with the example." << std::endl;
    std::cout << "=====================================================\n";
}

// END SNIPPET: demo
<|endoftext|>


<commit_before>
//https://www.cnblogs.com/cdh49/p/3602211.html

#include <iostream>

#include <fstream>

#include <string>

using namespace std;

extern "C"

{

#include <lua.h>

#include <lauxlib.h>

#include <lualib.h>
};

void TestLua2();

int main()

{
    TestLua2();

    return 0;
}

void TestLua2()

{
    lua_State* L = luaL_newstate();

    luaopen_base(L); //

    luaopen_table(L); //

    luaopen_package(L); //

    luaopen_io(L); //

    luaopen_string(L); //

    luaL_openlibs(L); //打开以上所有的lib

    int valueCPP = 1;

    // 将a值压入栈顶

    lua_pushnumber(L, valueCPP);

    // 命名栈顶的值

    lua_setglobal(L, "valueCPP");

    string str;

    while (true)

    {
        cout << "输入lua文件路径:" << endl;

        getline(cin, str, '\n');

        if (luaL_loadfile(L, str.c_str())

            || lua_pcall(L, 0, 0, 0))

        {
            const char* error = lua_tostring(L, -1);

            cout << string(error) << endl;
        }
    }
}
<commit_msg>linux cpp luatest loadstr close<commit_after>
//https://www.cnblogs.com/cdh49/p/3602211.html

#include <iostream>

#include <fstream>

#include <string>

using namespace std;

extern "C"

{

#include <lua.h>

#include <lauxlib.h>

#include <lualib.h>
};

void TestLua2();

int main()

{
    TestLua2();

    return 0;
}

void TestLua2()

{
    lua_State* L = luaL_newstate();

    luaopen_base(L); //

    luaopen_table(L); //

    luaopen_package(L); //

    luaopen_io(L); //

    luaopen_string(L); //

    luaL_openlibs(L); //打开以上所有的lib

    int valueCPP = 1;

    // 将a值压入栈顶

    lua_pushnumber(L, valueCPP);

    // 命名栈顶的值

    lua_setglobal(L, "valueCPP");

    string str;

    // while (true)
    luaL_loadstring(L, "print('c++ = ' .. valueCPP)");
    lua_pcall(L, 0, 0, 0);
    {
        cout << "输入lua文件路径:" << endl;

        // getline(cin, str, '\n');

        // if (luaL_loadfile(L, str.c_str())

        //     || lua_pcall(L, 0, 0, 0))

        // {
        //     const char* error = lua_tostring(L, -1);

        //     cout << string(error) << endl;
        // }
    }
    lua_close(L);
}
<|endoftext|>


<commit_before><commit_msg>Remove shadow effect in LinkItem since it nukes performance<commit_after><|endoftext|>


<commit_before>#include "fileurlreader.h"

#include <cstring>
#include <fstream>

#include "utils.h"

namespace newsboat {

FileUrlReader::FileUrlReader(const std::string& file)
	: filename(file)
{
}

std::string FileUrlReader::get_source()
{
	return filename;
}

nonstd::optional<std::string> FileUrlReader::reload()
{
	urls.clear();
	tags.clear();
	alltags.clear();

	std::vector<std::string> lines;
	std::string error_message;
	if (!utils::read_text_file(filename, lines, error_message)) {
		return strprintf::fmt(_("Error: Failed to read urls from file \"%s\" (%s)"),
				filename,
				error_message);
	}

	for (const std::string& line : lines) {
		// skip empty lines and comments
		if (line.empty() || line[0] == '#') {
			continue;
		}

		std::vector<std::string> tokens = utils::tokenize_quoted(line);
		if (tokens.empty()) {
			continue;
		}

		std::string url = tokens[0];
		urls.push_back(url);

		tokens.erase(tokens.begin());
		if (!tokens.empty()) {
			tags[url] = tokens;
			for (const auto& token : tokens) {
				alltags.insert(token);
			}
		}
	};

	return {};
}

nonstd::optional<std::string> FileUrlReader::write_config()
{
	std::fstream f;
	f.open(filename, std::fstream::out);
	if (!f.is_open()) {
		const auto error_message = strerror(errno);
		return strprintf::fmt(_("Error: Failed to open file \"%s\" (%s)"),
				filename,
				error_message);
	}

	for (const auto& url : urls) {
		f << url;
		if (tags[url].size() > 0) {
			for (const auto& tag : tags[url]) {
				f << " \"" << tag << "\"";
			}
		}
		f << std::endl;
	}

	return {};
}

}
<commit_msg>fixup! Return error from UrlReader::reload() when file cannot be opened<commit_after>#include "fileurlreader.h"

#include <cstring>
#include <fstream>

#include "utils.h"

namespace newsboat {

FileUrlReader::FileUrlReader(const std::string& file)
	: filename(file)
{
}

std::string FileUrlReader::get_source()
{
	return filename;
}

nonstd::optional<std::string> FileUrlReader::reload()
{
	urls.clear();
	tags.clear();
	alltags.clear();

	std::vector<std::string> lines;
	std::string error_message;
	if (!utils::read_text_file(filename, lines, error_message)) {
		return strprintf::fmt(_("Error: Failed to read URLs from file \"%s\" (%s)"),
				filename,
				error_message);
	}

	for (const std::string& line : lines) {
		// skip empty lines and comments
		if (line.empty() || line[0] == '#') {
			continue;
		}

		std::vector<std::string> tokens = utils::tokenize_quoted(line);
		if (tokens.empty()) {
			continue;
		}

		std::string url = tokens[0];
		urls.push_back(url);

		tokens.erase(tokens.begin());
		if (!tokens.empty()) {
			tags[url] = tokens;
			for (const auto& token : tokens) {
				alltags.insert(token);
			}
		}
	};

	return {};
}

nonstd::optional<std::string> FileUrlReader::write_config()
{
	std::fstream f;
	f.open(filename, std::fstream::out);
	if (!f.is_open()) {
		const auto error_message = strerror(errno);
		return strprintf::fmt(_("Error: Failed to open file \"%s\" (%s)"),
				filename,
				error_message);
	}

	for (const auto& url : urls) {
		f << url;
		if (tags[url].size() > 0) {
			for (const auto& tag : tags[url]) {
				f << " \"" << tag << "\"";
			}
		}
		f << std::endl;
	}

	return {};
}

}
<|endoftext|>


<commit_before>#pragma once

#include <cstdint>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include "blackhole/sink/files/rotation/naming/basename.hpp"
#include "blackhole/sink/files/rotation/naming/comparator.hpp"
#include "blackhole/sink/files/rotation/naming/helpers.hpp"

namespace blackhole {

namespace sink {

namespace rotation {

namespace placeholder {

inline char symbol() {
    return '%';
}

struct counter;

template<typename T>
struct traits;

template<>
struct traits<counter> {
    static char symbol() { return 'N'; }
};

}

struct counter_t {
    const std::string prefix;
    const std::string suffix;
    const uint width;

    counter_t(const std::string& prefix, const std::string& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    counter_t(std::string&& prefix, std::string&& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    bool operator ==(const counter_t& other) const {
        return prefix == other.prefix && suffix == other.suffix && width == other.width;
    }

    friend std::ostream& operator <<(std::ostream& stream, const counter_t& counter) {
        stream << "counter_t('" << counter.prefix << "', '" << counter.suffix << "', " << counter.width << ")";
        return stream;
    }

    bool valid() const {
        return width != 0;
    }

    //! I just leave these examples as mini documentation.
    /*!
     * pattern:     test.log.%Y%m%d.%N      %(filename)s.%N     %(filename)s.%Y%m%d.%N
     * basename:    test.log.%Y%m%d.%N      test.log.%N         test.log.%Y%m%d.%N
     * filename:    test.log.20140130.1     test.log.1          test.log.20140130.1
     * next:        test.log.20140130.2     test.log.2          test.log.20140130.2
     */
    std::string next(const std::string& filename) const {
        BOOST_ASSERT(static_cast<int>(filename.size()) -
                     static_cast<int>(prefix.size()) -
                     static_cast<int>(suffix.size()) >= 0);

        const std::string& counter =
                filename.substr(prefix.size(), filename.size() - prefix.size() - suffix.size());
        const uint value = cast(counter);
        std::ostringstream stream;
        stream << std::string(filename.begin(), filename.begin() + prefix.size())
               << std::setfill('0') << std::setw(width) << (value + 1)
               << std::string(filename.begin() + prefix.size() + std::max(width, naming::digits(value)),
                              filename.end());
        return stream.str();
    }

    static counter_t from_string(const std::string& pattern) {
        std::string prefix;
        std::string suffix;
        int width = 0;
        bool found = false;
        for (auto it = pattern.begin(); it != pattern.end(); ++it) {
            if (found) {
                suffix.push_back(*it);
            } else if (*it == '%') {
                it++;
                std::string value;

                for (; it != pattern.end(); ++it) {
                    if (*it == 'N') {
                        found = true;
                        break;
                    }

                    value.push_back(*it);
                    if (!std::isdigit(*it)) {
                        break;
                    }
                }

                if (found) {
                    width = value.empty() ? 1 : boost::lexical_cast<uint>(value);
                } else {
                    prefix.push_back('%');
                    prefix.append(value);
                }
            } else {
                prefix.push_back(*it);
            }
        }

        const auto& placeholders = available_placeholders();
        for (auto it = placeholders.begin(); it != placeholders.end(); ++it) {
            boost::replace_all(prefix, it->first, it->second);
            boost::replace_all(suffix, it->first, it->second);
        }

        return counter_t(std::move(prefix), std::move(suffix), width);
    }

private:
    static uint cast(const std::string& str, uint default_value = 0) {
        try {
            return boost::lexical_cast<uint>(str);
        } catch (const boost::bad_lexical_cast&) {
            return default_value;
        }
    }

    static const std::map<std::string, std::string>& available_placeholders() {
        static std::map<std::string, std::string> PLACEHOLDERS = {
            { "%Y", "YYYY" },
            { "%m", "mm" },
            { "%d", "dd" },
            { "%H", "HH" },
            { "%M", "MM" },
            { "%s", "ss" }
        };
        return PLACEHOLDERS;
    }
};

} // namespace rotation

} // namespace sink

} // namespace blackhole
<commit_msg>Excluded unused classes.<commit_after>#pragma once

#include <cstdint>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

#include "blackhole/sink/files/rotation/naming/basename.hpp"
#include "blackhole/sink/files/rotation/naming/comparator.hpp"
#include "blackhole/sink/files/rotation/naming/helpers.hpp"

namespace blackhole {

namespace sink {

namespace rotation {

struct counter_t {
    const std::string prefix;
    const std::string suffix;
    const uint width;

    counter_t(const std::string& prefix, const std::string& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    counter_t(std::string&& prefix, std::string&& suffix, uint width) :
        prefix(std::move(prefix)),
        suffix(std::move(suffix)),
        width(width)
    {}

    bool operator ==(const counter_t& other) const {
        return prefix == other.prefix && suffix == other.suffix && width == other.width;
    }

    friend std::ostream& operator <<(std::ostream& stream, const counter_t& counter) {
        stream << "counter_t('" << counter.prefix << "', '" << counter.suffix << "', " << counter.width << ")";
        return stream;
    }

    bool valid() const {
        return width != 0;
    }

    //! I just leave these examples as mini documentation.
    /*!
     * pattern:     test.log.%Y%m%d.%N      %(filename)s.%N     %(filename)s.%Y%m%d.%N
     * basename:    test.log.%Y%m%d.%N      test.log.%N         test.log.%Y%m%d.%N
     * filename:    test.log.20140130.1     test.log.1          test.log.20140130.1
     * next:        test.log.20140130.2     test.log.2          test.log.20140130.2
     */
    std::string next(const std::string& filename) const {
        BOOST_ASSERT(static_cast<int>(filename.size()) -
                     static_cast<int>(prefix.size()) -
                     static_cast<int>(suffix.size()) >= 0);

        const std::string& counter =
                filename.substr(prefix.size(), filename.size() - prefix.size() - suffix.size());
        const uint value = cast(counter);
        std::ostringstream stream;
        stream << std::string(filename.begin(), filename.begin() + prefix.size())
               << std::setfill('0') << std::setw(width) << (value + 1)
               << std::string(filename.begin() + prefix.size() + std::max(width, naming::digits(value)),
                              filename.end());
        return stream.str();
    }

    static counter_t from_string(const std::string& pattern) {
        std::string prefix;
        std::string suffix;
        int width = 0;
        bool found = false;
        for (auto it = pattern.begin(); it != pattern.end(); ++it) {
            if (found) {
                suffix.push_back(*it);
            } else if (*it == '%') {
                it++;
                std::string value;

                for (; it != pattern.end(); ++it) {
                    if (*it == 'N') {
                        found = true;
                        break;
                    }

                    value.push_back(*it);
                    if (!std::isdigit(*it)) {
                        break;
                    }
                }

                if (found) {
                    width = value.empty() ? 1 : boost::lexical_cast<uint>(value);
                } else {
                    prefix.push_back('%');
                    prefix.append(value);
                }
            } else {
                prefix.push_back(*it);
            }
        }

        const auto& placeholders = available_placeholders();
        for (auto it = placeholders.begin(); it != placeholders.end(); ++it) {
            boost::replace_all(prefix, it->first, it->second);
            boost::replace_all(suffix, it->first, it->second);
        }

        return counter_t(std::move(prefix), std::move(suffix), width);
    }

private:
    static uint cast(const std::string& str, uint default_value = 0) {
        try {
            return boost::lexical_cast<uint>(str);
        } catch (const boost::bad_lexical_cast&) {
            return default_value;
        }
    }

    static const std::map<std::string, std::string>& available_placeholders() {
        static std::map<std::string, std::string> PLACEHOLDERS = {
            { "%Y", "YYYY" },
            { "%m", "mm" },
            { "%d", "dd" },
            { "%H", "HH" },
            { "%M", "MM" },
            { "%s", "ss" }
        };
        return PLACEHOLDERS;
    }
};

} // namespace rotation

} // namespace sink

} // namespace blackhole
<|endoftext|>