<commit_before>#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"

#include <cstring>

#include "../can_datagram.h"

TEST_GROUP(CANDatagramTestGroup)
{
    can_datagram_t dt;
    uint8_t address_buffer[128];
    uint8_t data_buffer[128];

    void setup(void)
    {
        can_datagram_init(&dt);
        can_datagram_set_address_buffer(&dt, address_buffer);
        can_datagram_set_data_buffer(&dt, data_buffer, sizeof data_buffer);
    }
};

TEST(CANDatagramTestGroup, CanInitDatagram)
{
    /* fill the struct with garbage. */
    memset(&dt, 0xaa, sizeof dt);

    /* Inits the datagram */
    can_datagram_init(&dt);

    CHECK_EQUAL(0, dt.crc);
    CHECK_EQUAL(0, dt.destination_nodes_len);
    CHECK_EQUAL(0, dt.data_len);
}

TEST(CANDatagramTestGroup, CanSetDestinationAdressesBuffer)
{
    uint8_t buf[10];
    can_datagram_set_address_buffer(&dt, buf);

    POINTERS_EQUAL(buf, dt.destination_nodes);
}

TEST(CANDatagramTestGroup, CanSetDataBuffer)
{
    uint8_t buf[10];
    can_datagram_set_data_buffer(&dt, buf, sizeof buf);
    POINTERS_EQUAL(buf, dt.data);
}

TEST(CANDatagramTestGroup, CanComputeCRC)
{
    dt.destination_nodes_len = 1;
    dt.destination_nodes[0] = 14;
    dt.data_len = 1;
    dt.data[0] = 0x42;

    CHECK_EQUAL(0x80d8a447, can_datagram_compute_crc(&dt));
}

TEST_GROUP(CANDatagramInputTestGroup)
{
    can_datagram_t datagram;
    uint8_t address_buffer[128];
    uint8_t data_buffer[128];

    void setup(void)
    {
        can_datagram_init(&datagram);
        can_datagram_set_address_buffer(&datagram, address_buffer);
        can_datagram_set_data_buffer(&datagram, data_buffer, sizeof data_buffer);
    }

    void input_data(uint8_t *data, size_t len)
    {
        for (int i = 0; i < len; ++i) {
            can_datagram_input_byte(&datagram, data[i]);
        }
    }
};

TEST(CANDatagramInputTestGroup, CANReadCRC)
{
    uint8_t buf[] = {0xde, 0xad, 0xbe, 0xef};
    input_data(buf, sizeof buf);

    CHECK_EQUAL(0xdeadbeef, datagram.crc);
}

TEST(CANDatagramInputTestGroup, CanReadDestinationLength)
{
    uint8_t buf[] = {0x00, 0x00, 0x00, 0x00, 42};

    input_data(buf, sizeof buf);

    CHECK_EQUAL(42, datagram.destination_nodes_len);
}

TEST(CANDatagramInputTestGroup, CanReadDestinations)
{
    int i;

    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        5, // destination node list length
        2, 3 // destination nodes
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(2, datagram.destination_nodes[0]);
    CHECK_EQUAL(3, datagram.destination_nodes[1]);
}

TEST(CANDatagramInputTestGroup, CanReadDataLength)
{
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0xca, 0xfe, 0xba, 0xbe // data length
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(0xcafebabe, datagram.data_len);
}

TEST(CANDatagramInputTestGroup, CanReadData)
{
    // Data
    char *data = (char *)"Hello";
    int len = strlen(data);

    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0x00, 0x00, 0x00, len & 0xff // data length
    };

    input_data(buf, sizeof buf);

    for (int i = 0; i < len; ++i) {
        can_datagram_input_byte(&datagram, data[i]);
    }

    STRCMP_EQUAL(data, (char *)datagram.data);
}

TEST(CANDatagramInputTestGroup, EmptyDatagramIsNotComplete)
{
    CHECK_FALSE(can_datagram_is_complete(&datagram));
}

TEST(CANDatagramInputTestGroup, IsCompleteWhenAllDataAreRead)
{
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_TRUE(can_datagram_is_complete(&datagram));
}

TEST(CANDatagramInputTestGroup, IsInvalidOnCRCMismatch)
{
    int len = 1;
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        len >> 8,  // data length (MSB)
        len & 0xff,// data length (LSB)
        0x42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_FALSE(can_datagram_is_valid(&datagram));
}

TEST(CANDatagramInputTestGroup, IsValidWhenAllDataAreReadAndCRCMatches)
{
    uint8_t buf[] = {
        0x80, 0xd8, 0xa4, 0x47, // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_TRUE(can_datagram_is_valid(&datagram));
}

TEST(CANDatagramInputTestGroup, CRCIsComputedInMoreThanOneDestinationNodeAndData)
{
    uint8_t buf[] = {
        0x05, 0x23, 0xb7, 0x30,
        2, // destination node list length
        14, 15, // destination nodes
        0x00, 0x00, 0x00, 0x02, // data length
        0x42,0x43 // data
    };

    input_data(buf, sizeof buf);

    CHECK_TRUE(can_datagram_is_valid(&datagram));
}

TEST(CANDatagramInputTestGroup, DoesNotAppendMoreBytesThanDataLen)
{
    /** This test checks that if bytes arrive after the specified data length, they
     * are simply discarded. */
    int len = 1;
    uint8_t buf[] = {
        0x9a, 0x54, 0xb8, 0x63, // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42, // data
        0x43, 0x44 // garbage value
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(0x42, datagram.data[0]);
    CHECK_EQUAL(0, datagram.data[1]);
}

TEST(CANDatagramInputTestGroup, DoesNotOverflowDataBuffer)
{
    /** This test checks that if bytes arrive after the specified data length, they
     * are simply discarded. */

    // Pass a smaller size (5) to check overflow detection
    can_datagram_set_data_buffer(&datagram, data_buffer, 5);

    char data[] = "hello, world"; // too long to fit in buffer !
    int len = strlen(data);

    uint8_t buf[] = {
        0x9a, 0x54, 0xb8, 0x63, // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, len // data length
    };

    input_data(buf, sizeof buf);

    for (int i = 0; i < len; ++i) {
        can_datagram_input_byte(&datagram, data[i]);
    }

    /* Check that we respected the limit. */
    STRCMP_EQUAL("hello", (char *)datagram.data);
}

TEST(CANDatagramInputTestGroup, CanResetToStart)
{
    // Writes some part of a datagram, like after a hotplug
    for (int i = 0; i < 20; ++i) {
        can_datagram_input_byte(&datagram, 3);
    }

    // We see the start of a datagram and start inputing the beginning of a
    // valid packet.
    can_datagram_start(&datagram);

    int len = 1;
    uint8_t buf[] = {
        0xde, 0xad, 0xbe, 0xef,  // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(0xdeadbeef, datagram.crc);
    CHECK_EQUAL(1, datagram.destination_nodes_len);
    CHECK_EQUAL(14, datagram.destination_nodes[0]);
    CHECK_EQUAL(1, datagram.data_len);
    CHECK_EQUAL(42, datagram.data[0]);
}

TEST_GROUP(CANDatagramOutputTestGroup)
{
    can_datagram_t datagram;
    uint8_t address_buffer[128];
    uint8_t data_buffer[128];

    char output[64];
    int ret;

    void setup(void)
    {
        can_datagram_init(&datagram);
        can_datagram_set_address_buffer(&datagram, address_buffer);
        can_datagram_set_data_buffer(&datagram, data_buffer, sizeof data_buffer);
        memset(output, 0, sizeof output);
    }
};

TEST(CANDatagramOutputTestGroup, CanOutputCRC)
{
    datagram.crc = 0xdeadbeef;

    ret = can_datagram_output_bytes(&datagram, output, 4);

    BYTES_EQUAL(0xde, output[0]);
    BYTES_EQUAL(0xad, output[1]);
    BYTES_EQUAL(0xbe, output[2]);
    BYTES_EQUAL(0xef, output[3]);
    CHECK_EQUAL(4, ret);
}

TEST(CANDatagramOutputTestGroup, CanStopMidCRC)
{
    datagram.crc = 0xdeadbeef;

    // Only output 2 bytes
    ret = can_datagram_output_bytes(&datagram, output, 2);

    CHECK_EQUAL(2, ret);
    BYTES_EQUAL(0xde, output[0]);
    BYTES_EQUAL(0xad, output[1]);
    BYTES_EQUAL(0x00, output[2]);
}

TEST(CANDatagramOutputTestGroup, CanStopMidCRCAndRestart)
{
    datagram.crc = 0xdeadbeef;

    // Only output 2 bytes
    can_datagram_output_bytes(&datagram, output, 2);

    // Writes the next two bytes
    ret = can_datagram_output_bytes(&datagram, output, 2);

    CHECK_EQUAL(2, ret);
    BYTES_EQUAL(0xbe, output[0]);
    BYTES_EQUAL(0xef, output[1]);
    BYTES_EQUAL(0x00, output[2]);
}

TEST(CANDatagramOutputTestGroup, CanOutputDestinationNodeList)
{
    datagram.destination_nodes_len = 2;
    datagram.destination_nodes[0] = 42;
    datagram.destination_nodes[1] = 43;

    can_datagram_output_bytes(&datagram, output, 7);

    BYTES_EQUAL(2, output[4]);
    BYTES_EQUAL(42, output[5]);
    BYTES_EQUAL(43, output[6]);
}

TEST(CANDatagramOutputTestGroup, CanOutputDataLength)
{
    datagram.destination_nodes_len = 1;
    datagram.destination_nodes[0] = 12;

    datagram.data_len = 0xcafebabe;

    can_datagram_output_bytes(&datagram, output, 10);

    BYTES_EQUAL(0xca, output[6]);
    BYTES_EQUAL(0xfe, output[7]);
    BYTES_EQUAL(0xba, output[8]);
    BYTES_EQUAL(0xbe, output[9]);
}

TEST(CANDatagramOutputTestGroup, CanOutputData)
{
    datagram.destination_nodes_len = 1;
    datagram.destination_nodes[0] = 12;

    datagram.data_len = 2;
    datagram.data[0] = 42;
    datagram.data[1] = 43;

    can_datagram_output_bytes(&datagram, output, 12);

    BYTES_EQUAL(42, output[10]);
    BYTES_EQUAL(43, output[11]);
}

TEST(CANDatagramOutputTestGroup, IfWeStopEarlierBytesWrittenIsReturned)
{
    int ret;
    datagram.destination_nodes_len = 1;
    datagram.destination_nodes[0] = 12;

    datagram.data_len = 2;
    datagram.data[0] = 42;
    datagram.data[1] = 43;

    // Output the first 10 bytes
    can_datagram_output_bytes(&datagram, output, 10);

    // So now we only have two bytes to send, but we ask for more
    ret = can_datagram_output_bytes(&datagram, output, 10);

    CHECK_EQUAL(2, ret);
}
<commit_msg>Add test for incomplete datagram.<commit_after>#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"

#include <cstring>

#include "../can_datagram.h"

TEST_GROUP(CANDatagramTestGroup)
{
    can_datagram_t dt;
    uint8_t address_buffer[128];
    uint8_t data_buffer[128];

    void setup(void)
    {
        can_datagram_init(&dt);
        can_datagram_set_address_buffer(&dt, address_buffer);
        can_datagram_set_data_buffer(&dt, data_buffer, sizeof data_buffer);
    }
};

TEST(CANDatagramTestGroup, CanInitDatagram)
{
    /* fill the struct with garbage. */
    memset(&dt, 0xaa, sizeof dt);

    /* Inits the datagram */
    can_datagram_init(&dt);

    CHECK_EQUAL(0, dt.crc);
    CHECK_EQUAL(0, dt.destination_nodes_len);
    CHECK_EQUAL(0, dt.data_len);
}

TEST(CANDatagramTestGroup, CanSetDestinationAdressesBuffer)
{
    uint8_t buf[10];
    can_datagram_set_address_buffer(&dt, buf);

    POINTERS_EQUAL(buf, dt.destination_nodes);
}

TEST(CANDatagramTestGroup, CanSetDataBuffer)
{
    uint8_t buf[10];
    can_datagram_set_data_buffer(&dt, buf, sizeof buf);
    POINTERS_EQUAL(buf, dt.data);
}

TEST(CANDatagramTestGroup, CanComputeCRC)
{
    dt.destination_nodes_len = 1;
    dt.destination_nodes[0] = 14;
    dt.data_len = 1;
    dt.data[0] = 0x42;

    CHECK_EQUAL(0x80d8a447, can_datagram_compute_crc(&dt));
}

TEST_GROUP(CANDatagramInputTestGroup)
{
    can_datagram_t datagram;
    uint8_t address_buffer[128];
    uint8_t data_buffer[128];

    void setup(void)
    {
        can_datagram_init(&datagram);
        can_datagram_set_address_buffer(&datagram, address_buffer);
        can_datagram_set_data_buffer(&datagram, data_buffer, sizeof data_buffer);
    }

    void input_data(uint8_t *data, size_t len)
    {
        for (int i = 0; i < len; ++i) {
            can_datagram_input_byte(&datagram, data[i]);
        }
    }
};

TEST(CANDatagramInputTestGroup, CANReadCRC)
{
    uint8_t buf[] = {0xde, 0xad, 0xbe, 0xef};
    input_data(buf, sizeof buf);

    CHECK_EQUAL(0xdeadbeef, datagram.crc);
}

TEST(CANDatagramInputTestGroup, CanReadDestinationLength)
{
    uint8_t buf[] = {0x00, 0x00, 0x00, 0x00, 42};

    input_data(buf, sizeof buf);

    CHECK_EQUAL(42, datagram.destination_nodes_len);
}

TEST(CANDatagramInputTestGroup, CanReadDestinations)
{
    int i;

    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        5, // destination node list length
        2, 3 // destination nodes
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(2, datagram.destination_nodes[0]);
    CHECK_EQUAL(3, datagram.destination_nodes[1]);
}

TEST(CANDatagramInputTestGroup, CanReadDataLength)
{
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0xca, 0xfe, 0xba, 0xbe // data length
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(0xcafebabe, datagram.data_len);
}

TEST(CANDatagramInputTestGroup, CanReadData)
{
    // Data
    char *data = (char *)"Hello";
    int len = strlen(data);

    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0x00, 0x00, 0x00, len & 0xff // data length
    };

    input_data(buf, sizeof buf);

    for (int i = 0; i < len; ++i) {
        can_datagram_input_byte(&datagram, data[i]);
    }

    STRCMP_EQUAL(data, (char *)datagram.data);
}

TEST(CANDatagramInputTestGroup, EmptyDatagramIsNotComplete)
{
    CHECK_FALSE(can_datagram_is_complete(&datagram));
}

TEST(CANDatagramInputTestGroup, IsCompleteWhenAllDataAreRead)
{
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_TRUE(can_datagram_is_complete(&datagram));
}

TEST(CANDatagramInputTestGroup, IsNotCompleteWhenReadInProgress)
{
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42
    };

    input_data(&buf[0], 8);

    CHECK_FALSE(can_datagram_is_complete(&datagram));

    input_data(&buf[8], sizeof(buf) - 8);

    CHECK_TRUE(can_datagram_is_complete(&datagram));
}

TEST(CANDatagramInputTestGroup, IsInvalidOnCRCMismatch)
{
    int len = 1;
    uint8_t buf[] = {
        0x00, 0x00, 0x00, 0x00, // CRC
        1, // destination node list length
        3, // destination nodes
        len >> 8,  // data length (MSB)
        len & 0xff,// data length (LSB)
        0x42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_FALSE(can_datagram_is_valid(&datagram));
}

TEST(CANDatagramInputTestGroup, IsValidWhenAllDataAreReadAndCRCMatches)
{
    uint8_t buf[] = {
        0x80, 0xd8, 0xa4, 0x47, // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_TRUE(can_datagram_is_valid(&datagram));
}

TEST(CANDatagramInputTestGroup, CRCIsComputedInMoreThanOneDestinationNodeAndData)
{
    uint8_t buf[] = {
        0x05, 0x23, 0xb7, 0x30,
        2, // destination node list length
        14, 15, // destination nodes
        0x00, 0x00, 0x00, 0x02, // data length
        0x42,0x43 // data
    };

    input_data(buf, sizeof buf);

    CHECK_TRUE(can_datagram_is_valid(&datagram));
}

TEST(CANDatagramInputTestGroup, DoesNotAppendMoreBytesThanDataLen)
{
    /** This test checks that if bytes arrive after the specified data length, they
     * are simply discarded. */
    int len = 1;
    uint8_t buf[] = {
        0x9a, 0x54, 0xb8, 0x63, // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        0x42, // data
        0x43, 0x44 // garbage value
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(0x42, datagram.data[0]);
    CHECK_EQUAL(0, datagram.data[1]);
}

TEST(CANDatagramInputTestGroup, DoesNotOverflowDataBuffer)
{
    /** This test checks that if bytes arrive after the specified data length, they
     * are simply discarded. */

    // Pass a smaller size (5) to check overflow detection
    can_datagram_set_data_buffer(&datagram, data_buffer, 5);

    char data[] = "hello, world"; // too long to fit in buffer !
    int len = strlen(data);

    uint8_t buf[] = {
        0x9a, 0x54, 0xb8, 0x63, // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, len // data length
    };

    input_data(buf, sizeof buf);

    for (int i = 0; i < len; ++i) {
        can_datagram_input_byte(&datagram, data[i]);
    }

    /* Check that we respected the limit. */
    STRCMP_EQUAL("hello", (char *)datagram.data);
}

TEST(CANDatagramInputTestGroup, CanResetToStart)
{
    // Writes some part of a datagram, like after a hotplug
    for (int i = 0; i < 20; ++i) {
        can_datagram_input_byte(&datagram, 3);
    }

    // We see the start of a datagram and start inputing the beginning of a
    // valid packet.
    can_datagram_start(&datagram);

    int len = 1;
    uint8_t buf[] = {
        0xde, 0xad, 0xbe, 0xef,  // CRC
        1, // destination node list length
        14, // destination nodes
        0x00, 0x00, 0x00, 0x01, // data length
        42 // data
    };

    input_data(buf, sizeof buf);

    CHECK_EQUAL(0xdeadbeef, datagram.crc);
    CHECK_EQUAL(1, datagram.destination_nodes_len);
    CHECK_EQUAL(14, datagram.destination_nodes[0]);
    CHECK_EQUAL(1, datagram.data_len);
    CHECK_EQUAL(42, datagram.data[0]);
}

TEST_GROUP(CANDatagramOutputTestGroup)
{
    can_datagram_t datagram;
    uint8_t address_buffer[128];
    uint8_t data_buffer[128];

    char output[64];
    int ret;

    void setup(void)
    {
        can_datagram_init(&datagram);
        can_datagram_set_address_buffer(&datagram, address_buffer);
        can_datagram_set_data_buffer(&datagram, data_buffer, sizeof data_buffer);
        memset(output, 0, sizeof output);
    }
};

TEST(CANDatagramOutputTestGroup, CanOutputCRC)
{
    datagram.crc = 0xdeadbeef;

    ret = can_datagram_output_bytes(&datagram, output, 4);

    BYTES_EQUAL(0xde, output[0]);
    BYTES_EQUAL(0xad, output[1]);
    BYTES_EQUAL(0xbe, output[2]);
    BYTES_EQUAL(0xef, output[3]);
    CHECK_EQUAL(4, ret);
}

TEST(CANDatagramOutputTestGroup, CanStopMidCRC)
{
    datagram.crc = 0xdeadbeef;

    // Only output 2 bytes
    ret = can_datagram_output_bytes(&datagram, output, 2);

    CHECK_EQUAL(2, ret);
    BYTES_EQUAL(0xde, output[0]);
    BYTES_EQUAL(0xad, output[1]);
    BYTES_EQUAL(0x00, output[2]);
}

TEST(CANDatagramOutputTestGroup, CanStopMidCRCAndRestart)
{
    datagram.crc = 0xdeadbeef;

    // Only output 2 bytes
    can_datagram_output_bytes(&datagram, output, 2);

    // Writes the next two bytes
    ret = can_datagram_output_bytes(&datagram, output, 2);

    CHECK_EQUAL(2, ret);
    BYTES_EQUAL(0xbe, output[0]);
    BYTES_EQUAL(0xef, output[1]);
    BYTES_EQUAL(0x00, output[2]);
}

TEST(CANDatagramOutputTestGroup, CanOutputDestinationNodeList)
{
    datagram.destination_nodes_len = 2;
    datagram.destination_nodes[0] = 42;
    datagram.destination_nodes[1] = 43;

    can_datagram_output_bytes(&datagram, output, 7);

    BYTES_EQUAL(2, output[4]);
    BYTES_EQUAL(42, output[5]);
    BYTES_EQUAL(43, output[6]);
}

TEST(CANDatagramOutputTestGroup, CanOutputDataLength)
{
    datagram.destination_nodes_len = 1;
    datagram.destination_nodes[0] = 12;

    datagram.data_len = 0xcafebabe;

    can_datagram_output_bytes(&datagram, output, 10);

    BYTES_EQUAL(0xca, output[6]);
    BYTES_EQUAL(0xfe, output[7]);
    BYTES_EQUAL(0xba, output[8]);
    BYTES_EQUAL(0xbe, output[9]);
}

TEST(CANDatagramOutputTestGroup, CanOutputData)
{
    datagram.destination_nodes_len = 1;
    datagram.destination_nodes[0] = 12;

    datagram.data_len = 2;
    datagram.data[0] = 42;
    datagram.data[1] = 43;

    can_datagram_output_bytes(&datagram, output, 12);

    BYTES_EQUAL(42, output[10]);
    BYTES_EQUAL(43, output[11]);
}

TEST(CANDatagramOutputTestGroup, IfWeStopEarlierBytesWrittenIsReturned)
{
    int ret;
    datagram.destination_nodes_len = 1;
    datagram.destination_nodes[0] = 12;

    datagram.data_len = 2;
    datagram.data[0] = 42;
    datagram.data[1] = 43;

    // Output the first 10 bytes
    can_datagram_output_bytes(&datagram, output, 10);

    // So now we only have two bytes to send, but we ask for more
    ret = can_datagram_output_bytes(&datagram, output, 10);

    CHECK_EQUAL(2, ret);
}
<|endoftext|>


<commit_before>/**************************************************************************
* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
*                                                                        *
* Author: The ALICE Off-line Project.                                    *
* Contributors are mentioned in the code where appropriate.              *
*                                                                        *
* Permission to use, copy, modify and distribute this software and its   *
* documentation strictly for non-commercial purposes is hereby granted   *
* without fee, provided that the above copyright notice appears in all   *
* copies and that both the copyright notice and this permission notice   *
* appear in the supporting documentation. The authors make no claims     *
* about the suitability of this software for any purpose. It is          *
* provided "as is" without express or implied warranty.                  *
**************************************************************************/

/* $Id$ */

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Class for TRD reconstruction                                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include <TObjString.h>
#include <TObjArray.h>
#include <TTreeStream.h>
#include <TDirectory.h>

#include "AliRawReader.h"

#include "AliTRDReconstructor.h"
#include "AliTRDclusterizer.h"
#include "AliTRDrawData.h"
#include "AliTRDrawStreamBase.h"
#include "AliTRDdigitsManager.h"
#include "AliTRDtrackerV1.h"

#define SETFLG(n,f) ((n) |= f)
#define CLRFLG(n,f) ((n) &= ~f)

ClassImp(AliTRDReconstructor)

TClonesArray *AliTRDReconstructor::fgClusters = NULL;
TClonesArray *AliTRDReconstructor::fgTracklets = NULL;
Char_t const * AliTRDReconstructor::fgSteerNames[kNsteer] = {
  "DigitsConversion       "
 ,"Write Clusters         "
 ,"Write Online Tracklets "
 ,"Stand Alone Tracking   "
 ,"HLT Mode               "
 ,"Process Online Tracklets"
 ,"Debug Streaming        "
};
Char_t const * AliTRDReconstructor::fgSteerFlags[kNsteer] = {
  "dc"// digits conversion [false]
 ,"cw"// write clusters [true]
 ,"tw"// write online tracklets [false]
 ,"sa"// track seeding (stand alone tracking) [true]
 ,"hlt"// HLT reconstruction [false]
 ,"tp"// also use online tracklets for reconstruction [false]
 ,"deb"// Write debug stream [false]
};
Char_t const * AliTRDReconstructor::fgTaskNames[AliTRDrecoParam::kTRDreconstructionTasks] = {
  "Clusterizer"
 ,"Tracker"
 ,"PID"
};
Char_t const * AliTRDReconstructor::fgTaskFlags[AliTRDrecoParam::kTRDreconstructionTasks] = {
  "cl"
 ,"tr"
 ,"pd"
};
Int_t AliTRDReconstructor::fgNTimeBins = -1;

//_____________________________________________________________________________
AliTRDReconstructor::AliTRDReconstructor()
  :AliReconstructor()
  ,fSteerParam(0)
  ,fClusterizer(NULL)
{
  // setting default "ON" steering parameters
  // owner of debug streamers
  SETFLG(fSteerParam, kOwner);
  // write clusters [cw]
  SETFLG(fSteerParam, kWriteClusters);
  // track seeding (stand alone tracking) [sa]
  SETFLG(fSteerParam, kSeeding);

  memset(fDebugStream, 0, sizeof(TTreeSRedirector *) * AliTRDrecoParam::kTRDreconstructionTasks);
}

//_____________________________________________________________________________
AliTRDReconstructor::~AliTRDReconstructor()
{
  //
  // Destructor
  //

  if(fgClusters) {
    fgClusters->Delete();
    delete fgClusters;
    fgClusters = NULL;
  }
  if(fgTracklets) {
    fgTracklets->Delete();
    delete fgTracklets;
    fgTracklets = NULL;
  }
  if(fSteerParam&kOwner){
    for(Int_t itask = 0; itask < AliTRDrecoParam::kTRDreconstructionTasks; itask++)
      if(fDebugStream[itask]) delete fDebugStream[itask];
  }
  if(fClusterizer){
    delete fClusterizer;
    fClusterizer = NULL;
  }
}

//_____________________________________________________________________________
void AliTRDReconstructor::Init(){
  //
  // Init Options
  //
  SetOption(GetOption());
  Options(fSteerParam);

  if(!fClusterizer){
    fClusterizer = new AliTRDclusterizer(fgTaskNames[AliTRDrecoParam::kClusterizer], fgTaskNames[AliTRDrecoParam::kClusterizer]);
    fClusterizer->SetReconstructor(this);
  }

  // Make Debug Streams when Debug Streaming
  if(IsDebugStreaming()){
    for(Int_t task = 0; task < AliTRDrecoParam::kTRDreconstructionTasks; task++){
      TDirectory *savedir = gDirectory;
      fDebugStream[task] = new TTreeSRedirector(Form("TRD.Debug%s.root", fgTaskNames[task]));
      savedir->cd();
      SETFLG(fSteerParam, kOwner);
    }
  }
}

//_____________________________________________________________________________
void AliTRDReconstructor::ConvertDigits(AliRawReader *rawReader
              , TTree *digitsTree) const
{
  //
  // Convert raw data digits into digit objects in a root tree
  //

  //AliInfo("Convert raw data digits into digit objects [RawReader -> Digit TTree]");

  AliTRDrawData rawData;
  rawReader->Reset();
  rawReader->Select("TRD");
  rawData.OpenOutput();
  AliTRDrawStreamBase::SetRawStreamVersion(GetRecoParam()->GetRawStreamVersion()->Data());
  AliTRDdigitsManager *manager = rawData.Raw2Digits(rawReader);
  manager->MakeBranch(digitsTree);
  manager->WriteDigits();
  delete manager;

}

//_____________________________________________________________________________
void AliTRDReconstructor::Reconstruct(AliRawReader *rawReader
                                    , TTree *clusterTree) const
{
  //
  // Reconstruct clusters
  //

  //AliInfo("Reconstruct TRD clusters from RAW data [RawReader -> Cluster TTree]");

  rawReader->Reset();
  rawReader->Select("TRD");
  AliTRDrawStreamBase::SetRawStreamVersion(GetRecoParam()->GetRawStreamVersion()->Data());

  if(!fClusterizer){
    AliFatal("Clusterizer not available!");
    return;
  }

  fClusterizer->ResetRecPoints();

  fClusterizer->OpenOutput(clusterTree);
  fClusterizer->OpenTrackletOutput();
  fClusterizer->SetUseLabels(kFALSE);
  fClusterizer->Raw2ClustersChamber(rawReader);

  if(IsWritingClusters()) return;

  // take over ownership of clusters
  fgClusters = fClusterizer->RecPoints();
  fClusterizer->SetClustersOwner(kFALSE);

  // take over ownership of online tracklets
  fgTracklets = fClusterizer->TrackletsArray();
  fClusterizer->SetTrackletsOwner(kFALSE);

  fgNTimeBins = fClusterizer->GetNTimeBins();
}

//_____________________________________________________________________________
void AliTRDReconstructor::Reconstruct(TTree *digitsTree
                                    , TTree *clusterTree) const
{
  //
  // Reconstruct clusters
  //

  //AliInfo("Reconstruct TRD clusters from Digits [Digit TTree -> Cluster TTree]");

  if(!fClusterizer){
    AliFatal("Clusterizer not available!");
    return;
  }

  fClusterizer->ResetRecPoints();

  fClusterizer->OpenOutput(clusterTree);
  fClusterizer->ReadDigits(digitsTree);
  fClusterizer->MakeClusters();

  if(IsWritingClusters()) return;

  // take over ownership of clusters
  fgClusters = fClusterizer->RecPoints();
  fClusterizer->SetClustersOwner(kFALSE);

  // take over ownership of online tracklets
  fgTracklets = fClusterizer->TrackletsArray();
  fClusterizer->SetTrackletsOwner(kFALSE);

  fgNTimeBins = fClusterizer->GetNTimeBins();
}

//_____________________________________________________________________________
AliTracker *AliTRDReconstructor::CreateTracker() const
{
  //
  // Create a TRD tracker
  //

  //return new AliTRDtracker(NULL);
  AliTRDtrackerV1 *tracker = new AliTRDtrackerV1();
  tracker->SetReconstructor(this);
  return tracker;

}

//_____________________________________________________________________________
void AliTRDReconstructor::FillESD(TTree* /*digitsTree*/
        , TTree* /*clusterTree*/
        , AliESDEvent* /*esd*/) const
{
  //
  // Fill ESD
  //

}

//_____________________________________________________________________________
void AliTRDReconstructor::SetOption(Option_t *opt)
{
  //
  // Read option string into the steer param.
  //

  AliReconstructor::SetOption(opt);

  TString s(opt);
  TObjArray *opar = s.Tokenize(",");
  for(Int_t ipar=0; ipar<opar->GetEntriesFast(); ipar++){
    Bool_t processed = kFALSE;
    TString sopt(((TObjString*)(*opar)[ipar])->String());
    for(Int_t iopt=0; iopt<kNsteer; iopt++){
      if(!sopt.Contains(fgSteerFlags[iopt])) continue;
      SETFLG(fSteerParam, BIT(iopt));
      if(sopt.Contains("!")) CLRFLG(fSteerParam, BIT(iopt));
      processed = kTRUE;
      break;
    }
    if(processed) continue;

    AliWarning(Form("Unknown option flag %s.", sopt.Data()));
  }
}

//_____________________________________________________________________________
void AliTRDReconstructor::Options(UInt_t steer)
{
  //
  // Print the options
  //

  for(Int_t iopt=0; iopt<kNsteer; iopt++){
    AliDebugGeneral("AliTRDReconstructor", 1, Form(" %s[%s]%s", fgSteerNames[iopt], fgSteerFlags[iopt], steer ?(((steer>>iopt)&1)?" : ON":" : OFF"):""));
  }
}

<commit_msg>Revert some of the recent changes (Theo)<commit_after>/**************************************************************************
* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
*                                                                        *
* Author: The ALICE Off-line Project.                                    *
* Contributors are mentioned in the code where appropriate.              *
*                                                                        *
* Permission to use, copy, modify and distribute this software and its   *
* documentation strictly for non-commercial purposes is hereby granted   *
* without fee, provided that the above copyright notice appears in all   *
* copies and that both the copyright notice and this permission notice   *
* appear in the supporting documentation. The authors make no claims     *
* about the suitability of this software for any purpose. It is          *
* provided "as is" without express or implied warranty.                  *
**************************************************************************/

/* $Id$ */

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Class for TRD reconstruction                                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include <TObjString.h>
#include <TObjArray.h>
#include <TTreeStream.h>
#include <TDirectory.h>

#include "AliRawReader.h"

#include "AliTRDReconstructor.h"
#include "AliTRDclusterizer.h"
#include "AliTRDrawData.h"
#include "AliTRDrawStreamBase.h"
#include "AliTRDdigitsManager.h"
#include "AliTRDtrackerV1.h"

#define SETFLG(n,f) ((n) |= f)
#define CLRFLG(n,f) ((n) &= ~f)

ClassImp(AliTRDReconstructor)

TClonesArray *AliTRDReconstructor::fgClusters = NULL;
TClonesArray *AliTRDReconstructor::fgTracklets = NULL;
Char_t const * AliTRDReconstructor::fgSteerNames[kNsteer] = {
  "DigitsConversion       "
 ,"Write Clusters         "
 ,"Write Online Tracklets "
 ,"Stand Alone Tracking   "
 ,"HLT Mode               "
 ,"Process Online Tracklets"
 ,"Debug Streaming        "
};
Char_t const * AliTRDReconstructor::fgSteerFlags[kNsteer] = {
  "dc"// digits conversion [false]
 ,"cw"// write clusters [true]
 ,"tw"// write online tracklets [false]
 ,"sa"// track seeding (stand alone tracking) [true]
 ,"hlt"// HLT reconstruction [false]
 ,"tp"// also use online tracklets for reconstruction [false]
 ,"deb"// Write debug stream [false]
};
Char_t const * AliTRDReconstructor::fgTaskNames[AliTRDrecoParam::kTRDreconstructionTasks] = {
  "Clusterizer"
 ,"Tracker"
 ,"PID"
};
Char_t const * AliTRDReconstructor::fgTaskFlags[AliTRDrecoParam::kTRDreconstructionTasks] = {
  "cl"
 ,"tr"
 ,"pd"
};
Int_t AliTRDReconstructor::fgNTimeBins = -1;

//_____________________________________________________________________________
AliTRDReconstructor::AliTRDReconstructor()
  :AliReconstructor()
  ,fSteerParam(0)
  ,fClusterizer(NULL)
{
  // setting default "ON" steering parameters
  // owner of debug streamers
  SETFLG(fSteerParam, kOwner);
  // write clusters [cw]
  SETFLG(fSteerParam, kWriteClusters);
  // track seeding (stand alone tracking) [sa]
  SETFLG(fSteerParam, kSeeding);

  memset(fDebugStream, 0, sizeof(TTreeSRedirector *) * AliTRDrecoParam::kTRDreconstructionTasks);
}

//_____________________________________________________________________________
AliTRDReconstructor::~AliTRDReconstructor()
{
  //
  // Destructor
  //

  if(fgClusters) {
    fgClusters->Delete();
    delete fgClusters;
    fgClusters = NULL;
  }
  if(fgTracklets) {
    fgTracklets->Delete();
    delete fgTracklets;
    fgTracklets = NULL;
  }
  if(fSteerParam&kOwner){
    for(Int_t itask = 0; itask < AliTRDrecoParam::kTRDreconstructionTasks; itask++)
      if(fDebugStream[itask]) delete fDebugStream[itask];
  }
  if(fClusterizer){
    delete fClusterizer;
    fClusterizer = NULL;
  }
}

//_____________________________________________________________________________
void AliTRDReconstructor::Init(){
  //
  // Init Options
  //
  SetOption(GetOption());
  Options(fSteerParam);

  if(!fClusterizer){
    fClusterizer = new AliTRDclusterizer(fgTaskNames[AliTRDrecoParam::kClusterizer], fgTaskNames[AliTRDrecoParam::kClusterizer]);
    fClusterizer->SetReconstructor(this);
  }

  // Make Debug Streams when Debug Streaming
  if(IsDebugStreaming()){
    for(Int_t task = 0; task < AliTRDrecoParam::kTRDreconstructionTasks; task++){
      TDirectory *savedir = gDirectory;
      fDebugStream[task] = new TTreeSRedirector(Form("TRD.Debug%s.root", fgTaskNames[task]));
      savedir->cd();
      SETFLG(fSteerParam, kOwner);
    }
  }
}

//_____________________________________________________________________________
void AliTRDReconstructor::ConvertDigits(AliRawReader *rawReader
              , TTree *digitsTree) const
{
  //
  // Convert raw data digits into digit objects in a root tree
  //

  //AliInfo("Convert raw data digits into digit objects [RawReader -> Digit TTree]");

  AliTRDrawData rawData;
  rawReader->Reset();
  rawReader->Select("TRD");
  rawData.OpenOutput();
  AliTRDrawStreamBase::SetRawStreamVersion(GetRecoParam()->GetRawStreamVersion()->Data());
  AliTRDdigitsManager *manager = rawData.Raw2Digits(rawReader);
  manager->MakeBranch(digitsTree);
  manager->WriteDigits();
  delete manager;

}

//_____________________________________________________________________________
void AliTRDReconstructor::Reconstruct(AliRawReader *rawReader
                                    , TTree *clusterTree) const
{
  //
  // Reconstruct clusters
  //

  //AliInfo("Reconstruct TRD clusters from RAW data [RawReader -> Cluster TTree]");

  rawReader->Reset();
  rawReader->Select("TRD");
  AliTRDrawStreamBase::SetRawStreamVersion(GetRecoParam()->GetRawStreamVersion()->Data());

  if(!fClusterizer){
    AliFatal("Clusterizer not available!");
    return;
  }

  fClusterizer->ResetRecPoints();

  fClusterizer->OpenOutput(clusterTree);
  fClusterizer->OpenTrackletOutput();
  fClusterizer->SetUseLabels(kFALSE);
  fClusterizer->Raw2ClustersChamber(rawReader);

  if(IsWritingClusters()) return;

  // take over ownership of clusters
  fgClusters = fClusterizer->RecPoints();
  fClusterizer->SetClustersOwner(kFALSE);

  // take over ownership of online tracklets
  fgTracklets = fClusterizer->TrackletsArray();
  fClusterizer->SetTrackletsOwner(kFALSE);

  fgNTimeBins = fClusterizer->GetNTimeBins();
}

//_____________________________________________________________________________
void AliTRDReconstructor::Reconstruct(TTree *digitsTree
                                    , TTree *clusterTree) const
{
  //
  // Reconstruct clusters
  //

  //AliInfo("Reconstruct TRD clusters from Digits [Digit TTree -> Cluster TTree]");

  AliTRDclusterizer clusterer(fgTaskNames[AliTRDrecoParam::kClusterizer], fgTaskNames[AliTRDrecoParam::kClusterizer]);
  clusterer.SetReconstructor(this);
  clusterer.OpenOutput(clusterTree);
  clusterer.ReadDigits(digitsTree);
  clusterer.MakeClusters();

  if(IsWritingClusters()) return;

  // take over ownership of clusters
  fgClusters = clusterer.RecPoints();
  clusterer.SetClustersOwner(kFALSE);

  // take over ownership of online tracklets
  fgTracklets = clusterer.TrackletsArray();
  clusterer.SetTrackletsOwner(kFALSE);

  fgNTimeBins = clusterer.GetNTimeBins();

}

//_____________________________________________________________________________
AliTracker *AliTRDReconstructor::CreateTracker() const
{
  //
  // Create a TRD tracker
  //

  //return new AliTRDtracker(NULL);
  AliTRDtrackerV1 *tracker = new AliTRDtrackerV1();
  tracker->SetReconstructor(this);
  return tracker;

}

//_____________________________________________________________________________
void AliTRDReconstructor::FillESD(TTree* /*digitsTree*/
        , TTree* /*clusterTree*/
        , AliESDEvent* /*esd*/) const
{
  //
  // Fill ESD
  //

}

//_____________________________________________________________________________
void AliTRDReconstructor::SetOption(Option_t *opt)
{
  //
  // Read option string into the steer param.
  //

  AliReconstructor::SetOption(opt);

  TString s(opt);
  TObjArray *opar = s.Tokenize(",");
  for(Int_t ipar=0; ipar<opar->GetEntriesFast(); ipar++){
    Bool_t processed = kFALSE;
    TString sopt(((TObjString*)(*opar)[ipar])->String());
    for(Int_t iopt=0; iopt<kNsteer; iopt++){
      if(!sopt.Contains(fgSteerFlags[iopt])) continue;
      SETFLG(fSteerParam, BIT(iopt));
      if(sopt.Contains("!")) CLRFLG(fSteerParam, BIT(iopt));
      processed = kTRUE;
      break;
    }
    if(processed) continue;

    AliWarning(Form("Unknown option flag %s.", sopt.Data()));
  }
}

//_____________________________________________________________________________
void AliTRDReconstructor::Options(UInt_t steer)
{
  //
  // Print the options
  //

  for(Int_t iopt=0; iopt<kNsteer; iopt++){
    AliDebugGeneral("AliTRDReconstructor", 1, Form(" %s[%s]%s", fgSteerNames[iopt], fgSteerFlags[iopt], steer ?(((steer>>iopt)&1)?" : ON":" : OFF"):""));
  }
}

<|endoftext|>


<commit_before>//===- DAGISelEmitter.cpp - Generate an instruction selector --------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend emits a DAG instruction selector.
//
//===----------------------------------------------------------------------===//

#include "DAGISelEmitter.h"
#include "DAGISelMatcher.h"
#include "Record.h"
#include "llvm/Support/Debug.h"
using namespace llvm;

//===----------------------------------------------------------------------===//
// DAGISelEmitter Helper methods
//

/// getResultPatternCost - Compute the number of instructions for this pattern.
/// This is a temporary hack.  We should really include the instruction
/// latencies in this calculation.
static unsigned getResultPatternCost(TreePatternNode *P,
                                     CodeGenDAGPatterns &CGP) {
  if (P->isLeaf()) return 0;

  unsigned Cost = 0;
  Record *Op = P->getOperator();
  if (Op->isSubClassOf("Instruction")) {
    Cost++;
    CodeGenInstruction &II = CGP.getTargetInfo().getInstruction(Op);
    if (II.usesCustomInserter)
      Cost += 10;
  }
  for (unsigned i = 0, e = P->getNumChildren(); i != e; ++i)
    Cost += getResultPatternCost(P->getChild(i), CGP);
  return Cost;
}

/// getResultPatternCodeSize - Compute the code size of instructions for this
/// pattern.
static unsigned getResultPatternSize(TreePatternNode *P,
                                     CodeGenDAGPatterns &CGP) {
  if (P->isLeaf()) return 0;

  unsigned Cost = 0;
  Record *Op = P->getOperator();
  if (Op->isSubClassOf("Instruction")) {
    Cost += Op->getValueAsInt("CodeSize");
  }
  for (unsigned i = 0, e = P->getNumChildren(); i != e; ++i)
    Cost += getResultPatternSize(P->getChild(i), CGP);
  return Cost;
}

//===----------------------------------------------------------------------===//
// Predicate emitter implementation.
//

void DAGISelEmitter::EmitPredicateFunctions(raw_ostream &OS) {
  OS << "\n// Predicate functions.\n";

  // Walk the pattern fragments, adding them to a map, which sorts them by
  // name.
  typedef std::map<std::string, std::pair<Record*, TreePattern*> > PFsByNameTy;
  PFsByNameTy PFsByName;

  for (CodeGenDAGPatterns::pf_iterator I = CGP.pf_begin(), E = CGP.pf_end();
       I != E; ++I)
    PFsByName.insert(std::make_pair(I->first->getName(), *I));

  for (PFsByNameTy::iterator I = PFsByName.begin(), E = PFsByName.end();
       I != E; ++I) {
    Record *PatFragRecord = I->second.first;// Record that derives from PatFrag.
    TreePattern *P = I->second.second;

    // If there is a code init for this fragment, emit the predicate code.
    std::string Code = PatFragRecord->getValueAsCode("Predicate");
    if (Code.empty()) continue;

    if (P->getOnlyTree()->isLeaf())
      OS << "inline bool Predicate_" << PatFragRecord->getName()
      << "(SDNode *N) const {\n";
    else {
      std::string ClassName =
        CGP.getSDNodeInfo(P->getOnlyTree()->getOperator()).getSDClassName();
      const char *C2 = ClassName == "SDNode" ? "N" : "inN";

      OS << "inline bool Predicate_" << PatFragRecord->getName()
         << "(SDNode *" << C2 << ") const {\n";
      if (ClassName != "SDNode")
        OS << "  " << ClassName << " *N = cast<" << ClassName << ">(inN);\n";
    }
    OS << Code << "\n}\n";
  }

  OS << "\n\n";
}

namespace {
// PatternSortingPredicate - return true if we prefer to match LHS before RHS.
// In particular, we want to match maximal patterns first and lowest cost within
// a particular complexity first.
struct PatternSortingPredicate {
  PatternSortingPredicate(CodeGenDAGPatterns &cgp) : CGP(cgp) {}
  CodeGenDAGPatterns &CGP;

  bool operator()(const PatternToMatch *LHS, const PatternToMatch *RHS) {
    // Otherwise, if the patterns might both match, sort based on complexity,
    // which means that we prefer to match patterns that cover more nodes in the
    // input over nodes that cover fewer.
    unsigned LHSSize = LHS->getPatternComplexity(CGP);
    unsigned RHSSize = RHS->getPatternComplexity(CGP);
    if (LHSSize > RHSSize) return true;   // LHS -> bigger -> less cost
    if (LHSSize < RHSSize) return false;

    // If the patterns have equal complexity, compare generated instruction cost
    unsigned LHSCost = getResultPatternCost(LHS->getDstPattern(), CGP);
    unsigned RHSCost = getResultPatternCost(RHS->getDstPattern(), CGP);
    if (LHSCost < RHSCost) return true;
    if (LHSCost > RHSCost) return false;

    unsigned LHSPatSize = getResultPatternSize(LHS->getDstPattern(), CGP);
    unsigned RHSPatSize = getResultPatternSize(RHS->getDstPattern(), CGP);
    if (LHSPatSize < RHSPatSize) return true;
    if (LHSPatSize > RHSPatSize) return false;

    // Sort based on the UID of the pattern, giving us a deterministic ordering
    // if all other sorting conditions fail.
    assert(LHS == RHS || LHS->ID != RHS->ID);
    return LHS->ID < RHS->ID;
  }
};
}

void DAGISelEmitter::run(raw_ostream &OS) {
  EmitSourceFileHeader("DAG Instruction Selector for the " +
                       CGP.getTargetInfo().getName() + " target", OS);

  OS << "// *** NOTE: This file is #included into the middle of the target\n"
     << "// *** instruction selector class.  These functions are really "
     << "methods.\n\n";

  DEBUG(errs() << "\n\nALL PATTERNS TO MATCH:\n\n";
        for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(),
             E = CGP.ptm_end(); I != E; ++I) {
          errs() << "PATTERN: ";   I->getSrcPattern()->dump();
          errs() << "\nRESULT:  "; I->getDstPattern()->dump();
          errs() << "\n";
        });

  // FIXME: These are being used by hand written code, gross.
  EmitPredicateFunctions(OS);

  // Add all the patterns to a temporary list so we can sort them.
  std::vector<const PatternToMatch*> Patterns;
  for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(), E = CGP.ptm_end();
       I != E; ++I)
    Patterns.push_back(&*I);

  // We want to process the matches in order of minimal cost.  Sort the patterns
  // so the least cost one is at the start.
  std::stable_sort(Patterns.begin(), Patterns.end(),
                   PatternSortingPredicate(CGP));

  // Convert each variant of each pattern into a Matcher.
  std::vector<Matcher*> PatternMatchers;
  for (unsigned i = 0, e = Patterns.size(); i != e; ++i) {
    for (unsigned Variant = 0; ; ++Variant) {
      if (Matcher *M = ConvertPatternToMatcher(*Patterns[i], Variant, CGP))
        PatternMatchers.push_back(M);
      else
        break;
    }
  }

  Matcher *TheMatcher = new ScopeMatcher(&PatternMatchers[0],
                                         PatternMatchers.size());

  TheMatcher = OptimizeMatcher(TheMatcher, CGP);
  //Matcher->dump();
  EmitMatcherTable(TheMatcher, CGP, OS);
  delete TheMatcher;
}
<commit_msg>Check in a (disabled) failed attempt to improve the ordering of  patterns within the generated matcher.  This works great except that the sort fails because the relation defined isn't  transitive.  I have a much simpler solution coming next, but want to archive the code.<commit_after>//===- DAGISelEmitter.cpp - Generate an instruction selector --------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend emits a DAG instruction selector.
//
//===----------------------------------------------------------------------===//

#include "DAGISelEmitter.h"
#include "DAGISelMatcher.h"
#include "Record.h"
#include "llvm/Support/Debug.h"
using namespace llvm;

//===----------------------------------------------------------------------===//
// DAGISelEmitter Helper methods
//

/// getResultPatternCost - Compute the number of instructions for this pattern.
/// This is a temporary hack.  We should really include the instruction
/// latencies in this calculation.
static unsigned getResultPatternCost(TreePatternNode *P,
                                     CodeGenDAGPatterns &CGP) {
  if (P->isLeaf()) return 0;

  unsigned Cost = 0;
  Record *Op = P->getOperator();
  if (Op->isSubClassOf("Instruction")) {
    Cost++;
    CodeGenInstruction &II = CGP.getTargetInfo().getInstruction(Op);
    if (II.usesCustomInserter)
      Cost += 10;
  }
  for (unsigned i = 0, e = P->getNumChildren(); i != e; ++i)
    Cost += getResultPatternCost(P->getChild(i), CGP);
  return Cost;
}

/// getResultPatternCodeSize - Compute the code size of instructions for this
/// pattern.
static unsigned getResultPatternSize(TreePatternNode *P,
                                     CodeGenDAGPatterns &CGP) {
  if (P->isLeaf()) return 0;

  unsigned Cost = 0;
  Record *Op = P->getOperator();
  if (Op->isSubClassOf("Instruction")) {
    Cost += Op->getValueAsInt("CodeSize");
  }
  for (unsigned i = 0, e = P->getNumChildren(); i != e; ++i)
    Cost += getResultPatternSize(P->getChild(i), CGP);
  return Cost;
}

//===----------------------------------------------------------------------===//
// Predicate emitter implementation.
//

void DAGISelEmitter::EmitPredicateFunctions(raw_ostream &OS) {
  OS << "\n// Predicate functions.\n";

  // Walk the pattern fragments, adding them to a map, which sorts them by
  // name.
  typedef std::map<std::string, std::pair<Record*, TreePattern*> > PFsByNameTy;
  PFsByNameTy PFsByName;

  for (CodeGenDAGPatterns::pf_iterator I = CGP.pf_begin(), E = CGP.pf_end();
       I != E; ++I)
    PFsByName.insert(std::make_pair(I->first->getName(), *I));

  for (PFsByNameTy::iterator I = PFsByName.begin(), E = PFsByName.end();
       I != E; ++I) {
    Record *PatFragRecord = I->second.first;// Record that derives from PatFrag.
    TreePattern *P = I->second.second;

    // If there is a code init for this fragment, emit the predicate code.
    std::string Code = PatFragRecord->getValueAsCode("Predicate");
    if (Code.empty()) continue;

    if (P->getOnlyTree()->isLeaf())
      OS << "inline bool Predicate_" << PatFragRecord->getName()
      << "(SDNode *N) const {\n";
    else {
      std::string ClassName =
        CGP.getSDNodeInfo(P->getOnlyTree()->getOperator()).getSDClassName();
      const char *C2 = ClassName == "SDNode" ? "N" : "inN";

      OS << "inline bool Predicate_" << PatFragRecord->getName()
         << "(SDNode *" << C2 << ") const {\n";
      if (ClassName != "SDNode")
        OS << "  " << ClassName << " *N = cast<" << ClassName << ">(inN);\n";
    }
    OS << Code << "\n}\n";
  }

  OS << "\n\n";
}

/// CouldMatchSameInput - Return true if it is possible for these two patterns
/// to match the same input.  For example, (add reg, reg) and
///   (add reg, (mul ...)) could both match the same input.  Where this is
/// conservative, it falls back to returning true.
static bool CouldMatchSameInput(const TreePatternNode *N1,
                                const TreePatternNode *N2) {
  // If the types of the two nodes differ, they can't match the same thing.
  if (N1->getNumTypes() != N2->getNumTypes()) return false;
  for (unsigned i = 0, e = N1->getNumTypes(); i != e; ++i)
    if (N1->getType(i) != N2->getType(i))
      return false;

  // Handle the case when at least one is a leaf.
  if (N1->isLeaf()) {
    if (N2->isLeaf()) {
      // Handle leaf/leaf cases.  Register operands can match just about
      // anything, so we can only disambiguate a few things here.

      // If both operands are leaf integer nodes with different values, they
      // can't match the same thing.
      if (IntInit *II1 = dynamic_cast<IntInit*>(N1->getLeafValue()))
        if (IntInit *II2 = dynamic_cast<IntInit*>(N2->getLeafValue()))
          return II1->getValue() == II2->getValue();

      DefInit *DI1 = dynamic_cast<DefInit*>(N1->getLeafValue());
      DefInit *DI2 = dynamic_cast<DefInit*>(N2->getLeafValue());
      if (DI1 != 0 && DI2 != 0) {
        if (DI1->getDef()->isSubClassOf("ValueType") &&
            DI2->getDef()->isSubClassOf("ValueType"))
          return DI1 == DI2;
        if (DI1->getDef()->isSubClassOf("CondCode") &&
            DI2->getDef()->isSubClassOf("CondCode"))
          return DI1 == DI2;
      }

      // TODO: Regclass cannot match a condcode etc.

      // Otherwise, complex pattern could match anything, so just return a
      // conservative response.
      return true;
    }

    // Conservatively return true.  (imm) could match "7" for example, and GPR
    // can match anything.
    // TODO: could handle (add ...)  != "1" if we cared.
    return true;
  }

  // If N2 is a leaf and N1 isn't, check the other way.
  if (N2->isLeaf())
    return CouldMatchSameInput(N2, N1);

  // Now we know neither node is a leaf.  If the two patterns have different
  // number of children or different operators, they can't both match.
  Record *Op1 = N1->getOperator(), *Op2 = N1->getOperator();

  if (Op1 != Op2 || N1->getNumChildren() != N2->getNumChildren())
    return false;

  // If a child prevents the two patterns from matching, use that.
  for (unsigned i = 0, e = N1->getNumChildren(); i != e; ++i)
    if (!CouldMatchSameInput(N1->getChild(i), N2->getChild(i)))
      return false;

  // Otherwise, it looks like they could both match the same thing.
  return true;
}

/// GetSourceMatchPreferenceOrdering - The two input patterns are guaranteed to
/// not match the same input.  Decide which pattern we'd prefer to match first
/// in order to reduce compile time.  This sorting predicate is used to improve
/// compile time so that we try to match scalar operations before vector
/// operations since scalar operations are much more common in practice.
///
/// This returns -1 if we prefer to match N1 before N2, 1 if we prefer to match
/// N2 before N1 or 0 if no preference.
///
static int GetSourceMatchPreferenceOrdering(const TreePatternNode *N1,
                                            const TreePatternNode *N2) {
  // The primary thing we sort on here is to get ints before floats and scalars
  // before vectors.
  for (unsigned i = 0, e = std::min(N1->getNumTypes(), N2->getNumTypes());
       i != e; ++i)
    if (N1->getType(i) != N2->getType(i)) {
      MVT::SimpleValueType V1 = N1->getType(i), V2 = N2->getType(i);
      if (MVT(V1).isVector() != MVT(V2).isVector())
        return MVT(V1).isVector() ? 1 : -1;

      if (MVT(V1).isFloatingPoint() != MVT(V2).isFloatingPoint())
        return MVT(V1).isFloatingPoint() ? 1 : -1;
    }

  for (unsigned i = 0, e = std::min(N1->getNumChildren(), N2->getNumChildren());
       i != e; ++i)
    if (int Res = GetSourceMatchPreferenceOrdering(N1->getChild(i),
                                                   N2->getChild(i)))
      return Res;
  return 0;
}

namespace {
// PatternSortingPredicate - return true if we prefer to match LHS before RHS.
// In particular, we want to match maximal patterns first and lowest cost within
// a particular complexity first.
struct PatternSortingPredicate {
  PatternSortingPredicate(CodeGenDAGPatterns &cgp) : CGP(cgp) {}
  CodeGenDAGPatterns &CGP;

  bool operator()(const PatternToMatch *LHS, const PatternToMatch *RHS) {
    const TreePatternNode *LHSSrc = LHS->getSrcPattern();
    const TreePatternNode *RHSSrc = RHS->getSrcPattern();

    // If the patterns are guaranteed to not match at the same time and we
    // prefer to match one before the other (for compile time reasons) use this
    // preference as our discriminator.
    if (0 && !CouldMatchSameInput(LHSSrc, RHSSrc)) {
      int Ordering = GetSourceMatchPreferenceOrdering(LHSSrc, RHSSrc);
      if (Ordering != 0) {
        if (Ordering == -1) {
          errs() << "SORT: " << *LHSSrc << "\n";
          errs() << "NEXT: " << *RHSSrc << "\n\n";
        } else {
          errs() << "SORT: " << *RHSSrc << "\n";
          errs() << "NEXT: " << *LHSSrc << "\n\n";
        }
      }

      if (Ordering == -1) return true;
      if (Ordering == 1) return false;
    }

    // Otherwise, if the patterns might both match, sort based on complexity,
    // which means that we prefer to match patterns that cover more nodes in the
    // input over nodes that cover fewer.
    unsigned LHSSize = LHS->getPatternComplexity(CGP);
    unsigned RHSSize = RHS->getPatternComplexity(CGP);
    if (LHSSize > RHSSize) return true;   // LHS -> bigger -> less cost
    if (LHSSize < RHSSize) return false;

    // If the patterns have equal complexity, compare generated instruction cost
    unsigned LHSCost = getResultPatternCost(LHS->getDstPattern(), CGP);
    unsigned RHSCost = getResultPatternCost(RHS->getDstPattern(), CGP);
    if (LHSCost < RHSCost) return true;
    if (LHSCost > RHSCost) return false;

    unsigned LHSPatSize = getResultPatternSize(LHS->getDstPattern(), CGP);
    unsigned RHSPatSize = getResultPatternSize(RHS->getDstPattern(), CGP);
    if (LHSPatSize < RHSPatSize) return true;
    if (LHSPatSize > RHSPatSize) return false;

    // Sort based on the UID of the pattern, giving us a deterministic ordering
    // if all other sorting conditions fail.
    assert(LHS == RHS || LHS->ID != RHS->ID);
    return LHS->ID < RHS->ID;
  }
};
}

void DAGISelEmitter::run(raw_ostream &OS) {
  EmitSourceFileHeader("DAG Instruction Selector for the " +
                       CGP.getTargetInfo().getName() + " target", OS);

  OS << "// *** NOTE: This file is #included into the middle of the target\n"
     << "// *** instruction selector class.  These functions are really "
     << "methods.\n\n";

  DEBUG(errs() << "\n\nALL PATTERNS TO MATCH:\n\n";
        for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(),
             E = CGP.ptm_end(); I != E; ++I) {
          errs() << "PATTERN: ";   I->getSrcPattern()->dump();
          errs() << "\nRESULT:  "; I->getDstPattern()->dump();
          errs() << "\n";
        });

  // FIXME: These are being used by hand written code, gross.
  EmitPredicateFunctions(OS);

  // Add all the patterns to a temporary list so we can sort them.
  std::vector<const PatternToMatch*> Patterns;
  for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(), E = CGP.ptm_end();
       I != E; ++I)
    Patterns.push_back(&*I);

  // We want to process the matches in order of minimal cost.  Sort the patterns
  // so the least cost one is at the start.
  std::sort(Patterns.begin(), Patterns.end(), PatternSortingPredicate(CGP));

  // Convert each variant of each pattern into a Matcher.
  std::vector<Matcher*> PatternMatchers;
  for (unsigned i = 0, e = Patterns.size(); i != e; ++i) {
    for (unsigned Variant = 0; ; ++Variant) {
      if (Matcher *M = ConvertPatternToMatcher(*Patterns[i], Variant, CGP))
        PatternMatchers.push_back(M);
      else
        break;
    }
  }

  Matcher *TheMatcher = new ScopeMatcher(&PatternMatchers[0],
                                         PatternMatchers.size());

  TheMatcher = OptimizeMatcher(TheMatcher, CGP);
  //Matcher->dump();
  EmitMatcherTable(TheMatcher, CGP, OS);
  delete TheMatcher;
}
<|endoftext|>


<commit_before>/**
* @file FixedSet.hpp
* @brief The FixedSet class.
* @author Dominique LaSalle <dominique@solidlake.com>
* Copyright 2017, Solid Lake LLC
* @version 1
* @date 2017-10-19
*/

#ifndef SOLIDUTILS_INCLUDE_FIXEDSET_HPP
#define SOLIDUTILS_INCLUDE_FIXEDSET_HPP

#include "Array.hpp"

namespace sl
{

/**
* @brief The FixedSet class provides a set implementation which allows for
* insertion, querying, and deletion in constant time. While std::unordered_set
* may give constant time complexity of these operations through a hash table,
* this does so through fixed size dense vector (no hashing).
*
* @tparam T The type of element to store.
*/
template <typename T>
class FixedSet
{
  public:
    static constexpr T const NULL_INDEX = static_cast<T>(-1);

    /**
    * @brief Create a new empty fixed set.
    *
    * @param size The size of the set.
    */
    FixedSet(
        size_t const size) :
      m_size(0),
      m_data(size),
      m_index(size, NULL_INDEX)
    {
      // do nothing
    }

    /**
    * @brief Check if an element exists in this set.
    *
    * @param element The element.
    *
    * @return The element.
    */
    bool has(
        T const element) const noexcept
    {
      size_t const index = static_cast<size_t>(element);

      ASSERT_LESS(index, m_index.size());

      return m_index[index] != NULL_INDEX;
    }

    /**
    * @brief Add an element to this set.
    *
    * @param element The element to add.
    */
    void add(
        T const element) noexcept
    {
      size_t const index = static_cast<size_t>(element);

      ASSERT_LESS(index, m_index.size());
      ASSERT_EQUAL(m_index[index], NULL_INDEX);

      m_data[m_size] = element;
      m_index[index] = m_size;

      ++m_size;
    }

    /**
    * @brief Remove an element from this set.
    *
    * @param element The element to remove.
    */
    void remove(
        T const element) noexcept
    {
      size_t const index = static_cast<size_t>(element);

      ASSERT_LESS(index, m_index.size());
      ASSERT_NOTEQUAL(m_index[index], NULL_INDEX);

      --m_size;
      T const swap = m_data[m_size];
      size_t const place = m_index[index];
      m_data[place] = swap;
      m_index[swap] = index;
      m_index[index] = NULL_INDEX;
    }

    /**
    * @brief Get the underlying array.
    *
    * @return The data.
    */
    T * data() noexcept
    {
      return m_data.data();
    }

    /**
    * @brief Get the underlying array.
    *
    * @return The data.
    */
    T const * data() const noexcept
    {
      return m_data.data();
    }

    /**
    * @brief Get the number of elements in the set.
    *
    * @return The number of elements.
    */
    size_t size() const noexcept
    {
      return m_size;
    }

    /**
    * @brief Get the beginning iterator.
    *
    * @return The iterator/pointer.
    */
    T const * begin() const noexcept
    {
      return m_data.begin();
    }

    /**
    * @brief Get the end iterator.
    *
    * @return The iterator/pointer.
    */
    T const * end() const noexcept
    {
      // we want to return the set's size, and not the array's.
      return m_data.begin() + m_size;
    }

    /**
    * @brief Get the beginning iterator (mutable).
    *
    * @return The iterator/pointer.
    */
    T * begin() noexcept
    {
      return m_data.begin();
    }

    /**
    * @brief Get the end iterator (mutable).
    *
    * @return The iterator/pointer.
    */
    T * end() noexcept
    {
      // we want to return the set's size, and not the array's.
      return m_data.begin() + m_size;
    }

  private:
    size_t m_size;
    Array<T> m_data;
    Array<T> m_index;
};

}

#endif
<commit_msg>Fix FixedSet::remove() indexing<commit_after>/**
* @file FixedSet.hpp
* @brief The FixedSet class.
* @author Dominique LaSalle <dominique@solidlake.com>
* Copyright 2017, Solid Lake LLC
* @version 1
* @date 2017-10-19
*/

#ifndef SOLIDUTILS_INCLUDE_FIXEDSET_HPP
#define SOLIDUTILS_INCLUDE_FIXEDSET_HPP

#include "Array.hpp"

namespace sl
{

/**
* @brief The FixedSet class provides a set implementation which allows for
* insertion, querying, and deletion in constant time. While std::unordered_set
* may give constant time complexity of these operations through a hash table,
* this does so through fixed size dense vector (no hashing).
*
* @tparam T The type of element to store.
*/
template <typename T>
class FixedSet
{
  public:
    static constexpr T const NULL_INDEX = static_cast<T>(-1);

    /**
    * @brief Create a new empty fixed set.
    *
    * @param size The size of the set.
    */
    FixedSet(
        size_t const size) :
      m_size(0),
      m_data(size),
      m_index(size, NULL_INDEX)
    {
      // do nothing
    }

    /**
    * @brief Check if an element exists in this set.
    *
    * @param element The element.
    *
    * @return The element.
    */
    bool has(
        T const element) const noexcept
    {
      size_t const index = static_cast<size_t>(element);

      ASSERT_LESS(index, m_index.size());

      return m_index[index] != NULL_INDEX;
    }

    /**
    * @brief Add an element to this set.
    *
    * @param element The element to add.
    */
    void add(
        T const element) noexcept
    {
      size_t const index = static_cast<size_t>(element);

      ASSERT_LESS(index, m_index.size());
      ASSERT_EQUAL(m_index[index], NULL_INDEX);

      m_data[m_size] = element;
      m_index[index] = m_size;

      ++m_size;
    }

    /**
    * @brief Remove an element from this set.
    *
    * @param element The element to remove.
    */
    void remove(
        T const element) noexcept
    {
      size_t const index = static_cast<size_t>(element);

      ASSERT_LESS(index, m_index.size());
      ASSERT_NOTEQUAL(m_index[index], NULL_INDEX);

      --m_size;
      T const swap = m_data[m_size];
      size_t const place = m_index[index];
      m_data[place] = swap;
      m_index[swap] = place;
      m_index[index] = NULL_INDEX;
    }

    /**
    * @brief Get the underlying array.
    *
    * @return The data.
    */
    T * data() noexcept
    {
      return m_data.data();
    }

    /**
    * @brief Get the underlying array.
    *
    * @return The data.
    */
    T const * data() const noexcept
    {
      return m_data.data();
    }

    /**
    * @brief Get the number of elements in the set.
    *
    * @return The number of elements.
    */
    size_t size() const noexcept
    {
      return m_size;
    }

    /**
    * @brief Get the beginning iterator.
    *
    * @return The iterator/pointer.
    */
    T const * begin() const noexcept
    {
      return m_data.begin();
    }

    /**
    * @brief Get the end iterator.
    *
    * @return The iterator/pointer.
    */
    T const * end() const noexcept
    {
      // we want to return the set's size, and not the array's.
      return m_data.begin() + m_size;
    }

    /**
    * @brief Get the beginning iterator (mutable).
    *
    * @return The iterator/pointer.
    */
    T * begin() noexcept
    {
      return m_data.begin();
    }

    /**
    * @brief Get the end iterator (mutable).
    *
    * @return The iterator/pointer.
    */
    T * end() noexcept
    {
      // we want to return the set's size, and not the array's.
      return m_data.begin() + m_size;
    }

  private:
    size_t m_size;
    Array<T> m_data;
    Array<T> m_index;
};

}

#endif
<|endoftext|>


<commit_before>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#include "allocore/ui/al_PresetSequencer.hpp"
#include "allocore/ui/al_SequenceRecorder.hpp"
#include "allocore/ui/al_Composition.hpp"
#include "allocore/io/al_File.hpp"

using namespace al;

void PresetSequencer::playSequence(std::string sequenceName)
{
	stopSequence();
	mSequenceLock.lock();
	//		while (!mSteps.empty()) {
	//			mSteps.pop();
	//		}
	if (sequenceName.size() > 0) {
		std::queue<Step> steps = loadSequence(sequenceName);
		mSteps = steps;
	}
	mRunning = true;
	mSequenceLock.unlock();
	mCurrentSequence = sequenceName;
	{
		std::unique_lock<std::mutex> lk(mPlayWaitLock);
		mSequencerThread = new std::thread(PresetSequencer::sequencerFunction, this);
		mPlayWaitVariable.wait(lk);
	}

//	std::thread::id seq_thread_id = mSequencerThread->get_id();
//	std::cout << "Preset Sequencer thread id: " << std::hex << seq_thread_id << std::endl;
}

void PresetSequencer::stopSequence(bool triggerCallbacks)
{
	if (mRunning == true) {
		mRunning = false;
		bool mCallbackStatus = false;
		if (!triggerCallbacks) {
			mCallbackStatus = mEndCallbackEnabled;
			enableEndCallback(false);
		}
		if (mSequencerThread) {
			std::thread *th = mSequencerThread;
			mSequencerThread = nullptr;
			th->join();
			delete th;
		}
		if (!triggerCallbacks) {
			enableEndCallback(mCallbackStatus);
		}
	}
}

bool PresetSequencer::archiveSequence(std::string sequenceName, bool overwrite)
{
	bool ok = true;
	std::string fullPath = buildFullPath(sequenceName) + "_archive";
	if (mPresetHandler == nullptr) {
		std::cerr << "A Preset Handler must be registered to store sequences. Aborting." << std::endl;
		return false;
	}
	if (overwrite) {
		if(File::isDirectory(fullPath)) {
			if (!Dir::removeRecursively(fullPath)) {
				std::cout << "Error removing directory: " << fullPath << " aborting sequence archiving." << std::endl;
				return false;
			}
		} else {
			if (File::remove(fullPath) != 0) {
				std::cout << "Error removing file: " << fullPath << " aborting sequence archiving." << std::endl;
				return false;
			}
		}
		if (!Dir::make(fullPath)) {
			std::cout << "Error creating sequence archive directory " << fullPath << std::endl;
			return false;
		}
	} else {
		int counter = 0;
		while (File::isDirectory(fullPath)) {
			std::string newName = sequenceName + "_" + std::to_string(counter++);
			fullPath = buildFullPath(newName) + "_archive";
			if (counter == 0) { // We've wrapped and run out of names...
				std::cout << "Out of names for sequence archive." << std::endl;
				return false;
			}
		}
		if (!Dir::make(fullPath)) {
			std::cout << "Error creating sequence archive directory " << fullPath << std::endl;
			return false;
		}
	}
	std::queue<Step> steps = loadSequence(sequenceName);
	while(steps.size() > 0) {
		Step &step = steps.front();
		std::string presetFilename = mPresetHandler->getCurrentPath() + step.presetName + ".preset";
		if (!File::copy(presetFilename, fullPath)) {
			std::cout << "Error copying preset " << presetFilename << " when archiving." << std::endl;
			ok = false;
		}
		steps.pop();
	}
	if (!File::copy(buildFullPath(sequenceName), fullPath)) {
		std::cout << "Error copying sequence " << sequenceName << " when archiving." << std::endl;
		ok = false;
	}

	return ok;
}

std::vector<std::string> al::PresetSequencer::getSequenceList()
{
	std::vector<std::string> sequenceList;
	std::string path = mDirectory;
	if (mPresetHandler) {
		path = mPresetHandler->getCurrentPath();
	}
	Dir presetDir(path);
	while(presetDir.read()) {
		FileInfo info = presetDir.entry();
		if (info.type() == FileInfo::REG) {
			std::string fileName = info.name();
			if (fileName.find(".sequence") == fileName.size() - 9) {
				// Should do better checks, what if '.sequence' is not at the end...
				sequenceList.push_back(fileName.substr(0, fileName.size() - 9));
			}
		}
	}
	return sequenceList;
}

void PresetSequencer::sequencerFunction(al::PresetSequencer *sequencer)
{
	if (sequencer->mPresetHandler == nullptr) {
		std::cerr << "No preset handler registered. Can't run sequencer." << std::endl;
		return;
	}
	{
		std::lock_guard<std::mutex> lk(sequencer->mPlayWaitLock);
		if (sequencer->mBeginCallbackEnabled && sequencer->mBeginCallback != nullptr) {
			sequencer->mBeginCallback(sequencer, sequencer->mBeginCallbackData);
		}
		sequencer->mPlayWaitVariable.notify_one();
	}
	const int granularity = 10; // milliseconds
	sequencer->mSequenceLock.lock();
	auto sequenceStart = std::chrono::high_resolution_clock::now();
	auto targetTime = sequenceStart;
	while(sequencer->running() && sequencer->mSteps.size() > 0) {
		Step &step = sequencer->mSteps.front();
		sequencer->mPresetHandler->setMorphTime(step.delta);
		sequencer->mPresetHandler->recallPreset(step.presetName);
		std::cout << "PresetSequencer loading:" << step.presetName << std::endl;
		std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - sequenceStart).count() / 1000.0 << std::endl;
		float totalWaitTime = step.delta + step.duration;
		targetTime += std::chrono::microseconds((int) (totalWaitTime*1.0e6 - (granularity * 1.5 * 1.0e3)));

		while (std::chrono::high_resolution_clock::now() < targetTime) { // Granularity to allow more responsive stopping of composition playback
		  //std::cout << std::chrono::high_resolution_clock::to_time_t(targetTime)
		//	    << "---" << std::chrono::high_resolution_clock::to_time_t(std::chrono::high_resolution_clock::now()) << std::endl;
			std::this_thread::sleep_for(std::chrono::milliseconds(granularity));
			if (sequencer->mRunning == false) {
				targetTime = std::chrono::high_resolution_clock::now();
				break;
			}
		}
		sequencer->mSteps.pop();
		std::this_thread::sleep_until(targetTime);
		// std::this_thread::sleep_for(std::chrono::duration<float>(totalWaitTime));
	}
	sequencer->mPresetHandler->stopMorph();
//	std::cout << "Sequence finished." << std::endl;
	sequencer->mRunning = false;
	sequencer->mSequenceLock.unlock();
	if (sequencer->mEndCallbackEnabled && sequencer->mEndCallback != nullptr) {
		bool finished = sequencer->mSteps.size() == 0;
		sequencer->mEndCallback(finished, sequencer, sequencer->mEndCallbackData);
	}
}

void PresetSequencer::setHandlerSubDirectory(std::string subDir)
{
	if (mPresetHandler) {
		mPresetHandler->setSubDirectory(subDir);
	} else {
		std::cerr << "Error in PresetSequencer::setHandlerSubDirectory. PresetHandler not registered." << std::endl;
	}
}

std::queue<PresetSequencer::Step> PresetSequencer::loadSequence(std::string sequenceName)
{
	std::queue<Step> steps;
	std::string fullName = buildFullPath(sequenceName);
	std::ifstream f(fullName);
	if (!f.is_open()) {
		std::cout << "Could not open:" << fullName << std::endl;
		return steps;
	}

	std::string line;
	while (getline(f, line)) {
		if (line.substr(0, 2) == "::") {
			break;
		}
		//FIXME here and in other sequencers white space should be stripped out
		std::stringstream ss(line);
		std::string name, delta,duration;
		std::getline(ss, name, ':');
		std::getline(ss, delta, ':');
		std::getline(ss, duration, ':');
		std::cout << line << std::endl;
		if (name.size() > 0 && name[0] != '#') {
			Step step;
			step.presetName = name;
			step.delta = std::stof(delta);
			step.duration = std::stof(duration);
			steps.push(step);
			// std::cout << name  << ":" << delta << ":" << duration << std::endl;
		}
	}
	if (f.bad()) {
		std::cout << "Error reading:" << sequenceName << std::endl;
	}
	return steps;
}

void PresetSequencer::registerBeginCallback(std::function<void(PresetSequencer *sender, void *userData)> beginCallback,
                                            void *userData)
{
	mBeginCallback = beginCallback;
	mBeginCallbackData = userData;
	mBeginCallbackEnabled = true;
}

void PresetSequencer::registerEndCallback(std::function<void (bool, al::PresetSequencer *, void *)> endCallback,
                                          void *userData)
{
	// FIXME this data needs to be protected with a mutex
	mEndCallback = endCallback;
	mEndCallbackData = userData;
	mEndCallbackEnabled = true;
}

float PresetSequencer::getSequenceTotalDuration(std::string sequenceName)
{
	std::queue<Step> steps = loadSequence(sequenceName);
	float duration = 0.0f;
	while (steps.size() > 0) {
		const Step &step = steps.front();
		duration += step.delta + step.duration;
		steps.pop();
	}
	return duration;
}

void PresetSequencer::clearSteps()
{
	stopSequence();
	mSequenceLock.lock();
	while (!mSteps.empty()) {
		mSteps.pop();
	}
	mSequenceLock.unlock();
}

void PresetSequencer::appendStep(PresetSequencer::Step &newStep)
{
	mSequenceLock.lock();
	mSteps.push(newStep);
	mSequenceLock.unlock();
}

bool PresetSequencer::consumeMessage(osc::Message &m, std::string rootOSCPath)
{
	std::string basePath = rootOSCPath;
	if (mOSCsubPath.size() > 0) {
		basePath += "/" + mOSCsubPath;
	}
	if(m.addressPattern() == basePath && m.typeTags() == "s"){
		std::string val;
		m >> val;
		std::cout << "start sequence " << val << std::endl;
		playSequence(val);
		return true;
	} else if(m.addressPattern() == basePath + "/stop" ){
		std::cout << "stop sequence " << std::endl;
		stopSequence();
		return true;
	}
	return false;
}

std::string PresetSequencer::buildFullPath(std::string sequenceName)
{
	std::string fullName = mDirectory;
	if (mPresetHandler) {
		fullName = mPresetHandler->getCurrentPath();
	}
	if (fullName.back() != '/') {
		fullName += "/";
	}
	if (sequenceName.size() < 9 || sequenceName.substr(sequenceName.size() - 9) != ".sequence") {
		fullName += sequenceName + ".sequence";
	}
	return fullName;
}

// SequenceServer ----------------------------------------------------------------

SequenceServer::SequenceServer(std::string oscAddress, int oscPort) :
    mServer(nullptr), mRecorder(nullptr),
    mParamServer(nullptr),
    mOSCpath("/sequence")
{
	mServer = new osc::Recv(oscPort, oscAddress.c_str(), 0.001); // Is this 1ms wait OK?
	if (mServer) {
		mServer->handler(*this);
		mServer->start();
	} else {
		std::cout << "Error starting OSC server." << std::endl;
	}
}

SequenceServer::SequenceServer(ParameterServer &paramServer) :
    mServer(nullptr),
    mParamServer(&paramServer),
    mOSCpath("/sequence")
{
	paramServer.registerOSCListener(this);
}

SequenceServer::~SequenceServer()
{
//	std::cout << "~SequenceServer()" << std::endl;;
	if (mServer) {
		mServer->stop();
		delete mServer;
		mServer = nullptr;
	}
}

void SequenceServer::onMessage(osc::Message &m)
{
	if(m.addressPattern() == mOSCpath + "/last"){
		if (mSequencer && mRecorder) {
			std::cout << "start last recorder sequence " << mRecorder->lastSequenceName() << std::endl;
			mSequencer->setHandlerSubDirectory(mRecorder->lastSequenceSubDir());
			mSequencer->playSequence( mRecorder->lastSequenceName());
		} else {
			std::cerr << "SequenceRecorder and PresetSequencer must be registered to enable /*/last." << std::endl;
		}
	} else {
		for(osc::MessageConsumer *consumer: mConsumers) {
			if (consumer->consumeMessage(m, mOSCpath)) {
				break;
			}
		}
	}
}

SequenceServer &SequenceServer::registerMessageConsumer(osc::MessageConsumer &consumer) {
	mConsumers.push_back(&consumer);
	return *this;
}

SequenceServer &SequenceServer::registerRecorder(SequenceRecorder &recorder) {
	mRecorder = &recorder;
	mConsumers.push_back(static_cast<osc::MessageConsumer *>(&recorder));
	return *this;
}

SequenceServer &SequenceServer::registerSequencer(PresetSequencer &sequencer) {
	mSequencer = &sequencer;
	mConsumers.push_back(&sequencer);
	return *this;
}

void SequenceServer::print()
{
	if (mServer) {
		std::cout << "Sequence server listening on: " << mServer->address() << ":" << mServer->port() << std::endl;
		std::cout << "Communicating on path: " << mOSCpath << std::endl;
	}
	for (auto sender:mOSCSenders) {
		std::cout << sender->address() << ":" << sender->port() << std::endl;
	}
}

void SequenceServer::stopServer()
{
	if (mServer) {
		mServer->stop();
		delete mServer;
		mServer = nullptr;
	}
}

void SequenceServer::setAddress(std::string address)
{
	mOSCpath = address;
}

std::string SequenceServer::getAddress()
{
	return mOSCpath;
}

void SequenceServer::changeCallback(int value, void *sender, void *userData)
{
	SequenceServer *server = static_cast<SequenceServer *>(userData);
	Parameter *parameter = static_cast<Parameter *>(sender);
	server->notifyListeners(server->mOSCpath, value);
}

<commit_msg>Minor fix when directory missing for PresetSequencer<commit_after>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#include "allocore/ui/al_PresetSequencer.hpp"
#include "allocore/ui/al_SequenceRecorder.hpp"
#include "allocore/ui/al_Composition.hpp"
#include "allocore/io/al_File.hpp"

using namespace al;

void PresetSequencer::playSequence(std::string sequenceName)
{
	stopSequence();
	mSequenceLock.lock();
	//		while (!mSteps.empty()) {
	//			mSteps.pop();
	//		}
	if (sequenceName.size() > 0) {
		std::queue<Step> steps = loadSequence(sequenceName);
		mSteps = steps;
	}
	mRunning = true;
	mSequenceLock.unlock();
	mCurrentSequence = sequenceName;
	{
		std::unique_lock<std::mutex> lk(mPlayWaitLock);
		mSequencerThread = new std::thread(PresetSequencer::sequencerFunction, this);
		mPlayWaitVariable.wait(lk);
	}

//	std::thread::id seq_thread_id = mSequencerThread->get_id();
//	std::cout << "Preset Sequencer thread id: " << std::hex << seq_thread_id << std::endl;
}

void PresetSequencer::stopSequence(bool triggerCallbacks)
{
	if (mRunning == true) {
		mRunning = false;
		bool mCallbackStatus = false;
		if (!triggerCallbacks) {
			mCallbackStatus = mEndCallbackEnabled;
			enableEndCallback(false);
		}
		if (mSequencerThread) {
			std::thread *th = mSequencerThread;
			mSequencerThread = nullptr;
			th->join();
			delete th;
		}
		if (!triggerCallbacks) {
			enableEndCallback(mCallbackStatus);
		}
	}
}

bool PresetSequencer::archiveSequence(std::string sequenceName, bool overwrite)
{
	bool ok = true;
	std::string fullPath = buildFullPath(sequenceName) + "_archive";
	if (mPresetHandler == nullptr) {
		std::cerr << "A Preset Handler must be registered to store sequences. Aborting." << std::endl;
		return false;
	}
	if (overwrite) {
		if(File::isDirectory(fullPath)) {
			if (!Dir::removeRecursively(fullPath)) {
				std::cout << "Error removing directory: " << fullPath << " aborting sequence archiving." << std::endl;
				return false;
			}
		} else {
			if (File::remove(fullPath) != 0) {
				std::cout << "Error removing file: " << fullPath << " aborting sequence archiving." << std::endl;
				return false;
			}
		}
		if (!Dir::make(fullPath)) {
			std::cout << "Error creating sequence archive directory " << fullPath << std::endl;
			return false;
		}
	} else {
		int counter = 0;
		while (File::isDirectory(fullPath)) {
			std::string newName = sequenceName + "_" + std::to_string(counter++);
			fullPath = buildFullPath(newName) + "_archive";
			if (counter == 0) { // We've wrapped and run out of names...
				std::cout << "Out of names for sequence archive." << std::endl;
				return false;
			}
		}
		if (!Dir::make(fullPath)) {
			std::cout << "Error creating sequence archive directory " << fullPath << std::endl;
			return false;
		}
	}
	std::queue<Step> steps = loadSequence(sequenceName);
	while(steps.size() > 0) {
		Step &step = steps.front();
		std::string presetFilename = mPresetHandler->getCurrentPath() + step.presetName + ".preset";
		if (!File::copy(presetFilename, fullPath)) {
			std::cout << "Error copying preset " << presetFilename << " when archiving." << std::endl;
			ok = false;
		}
		steps.pop();
	}
	if (!File::copy(buildFullPath(sequenceName), fullPath)) {
		std::cout << "Error copying sequence " << sequenceName << " when archiving." << std::endl;
		ok = false;
	}

	return ok;
}

std::vector<std::string> al::PresetSequencer::getSequenceList()
{
	std::vector<std::string> sequenceList;
	std::string path = mDirectory;
	if (mPresetHandler) {
		path = mPresetHandler->getCurrentPath();
	}
    if (!File::isDirectory(path)) {
        Dir::make(path, true);
    }
	Dir presetDir(path);
	while(presetDir.read()) {
		FileInfo info = presetDir.entry();
		if (info.type() == FileInfo::REG) {
			std::string fileName = info.name();
			if (fileName.find(".sequence") == fileName.size() - 9) {
				// Should do better checks, what if '.sequence' is not at the end...
				sequenceList.push_back(fileName.substr(0, fileName.size() - 9));
			}
		}
	}
	return sequenceList;
}

void PresetSequencer::sequencerFunction(al::PresetSequencer *sequencer)
{
	if (sequencer->mPresetHandler == nullptr) {
		std::cerr << "No preset handler registered. Can't run sequencer." << std::endl;
		return;
	}
	{
		std::lock_guard<std::mutex> lk(sequencer->mPlayWaitLock);
		if (sequencer->mBeginCallbackEnabled && sequencer->mBeginCallback != nullptr) {
			sequencer->mBeginCallback(sequencer, sequencer->mBeginCallbackData);
		}
		sequencer->mPlayWaitVariable.notify_one();
	}
	const int granularity = 10; // milliseconds
	sequencer->mSequenceLock.lock();
	auto sequenceStart = std::chrono::high_resolution_clock::now();
	auto targetTime = sequenceStart;
	while(sequencer->running() && sequencer->mSteps.size() > 0) {
		Step &step = sequencer->mSteps.front();
		sequencer->mPresetHandler->setMorphTime(step.delta);
		sequencer->mPresetHandler->recallPreset(step.presetName);
		std::cout << "PresetSequencer loading:" << step.presetName << std::endl;
		std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - sequenceStart).count() / 1000.0 << std::endl;
		float totalWaitTime = step.delta + step.duration;
		targetTime += std::chrono::microseconds((int) (totalWaitTime*1.0e6 - (granularity * 1.5 * 1.0e3)));

		while (std::chrono::high_resolution_clock::now() < targetTime) { // Granularity to allow more responsive stopping of composition playback
		  //std::cout << std::chrono::high_resolution_clock::to_time_t(targetTime)
		//	    << "---" << std::chrono::high_resolution_clock::to_time_t(std::chrono::high_resolution_clock::now()) << std::endl;
			std::this_thread::sleep_for(std::chrono::milliseconds(granularity));
			if (sequencer->mRunning == false) {
				targetTime = std::chrono::high_resolution_clock::now();
				break;
			}
		}
		sequencer->mSteps.pop();
		std::this_thread::sleep_until(targetTime);
		// std::this_thread::sleep_for(std::chrono::duration<float>(totalWaitTime));
	}
	sequencer->mPresetHandler->stopMorph();
//	std::cout << "Sequence finished." << std::endl;
	sequencer->mRunning = false;
	sequencer->mSequenceLock.unlock();
	if (sequencer->mEndCallbackEnabled && sequencer->mEndCallback != nullptr) {
		bool finished = sequencer->mSteps.size() == 0;
		sequencer->mEndCallback(finished, sequencer, sequencer->mEndCallbackData);
	}
}

void PresetSequencer::setHandlerSubDirectory(std::string subDir)
{
	if (mPresetHandler) {
		mPresetHandler->setSubDirectory(subDir);
	} else {
		std::cerr << "Error in PresetSequencer::setHandlerSubDirectory. PresetHandler not registered." << std::endl;
	}
}

std::queue<PresetSequencer::Step> PresetSequencer::loadSequence(std::string sequenceName)
{
	std::queue<Step> steps;
	std::string fullName = buildFullPath(sequenceName);
	std::ifstream f(fullName);
	if (!f.is_open()) {
		std::cout << "Could not open:" << fullName << std::endl;
		return steps;
	}

	std::string line;
	while (getline(f, line)) {
		if (line.substr(0, 2) == "::") {
			break;
		}
		//FIXME here and in other sequencers white space should be stripped out
		std::stringstream ss(line);
		std::string name, delta,duration;
		std::getline(ss, name, ':');
		std::getline(ss, delta, ':');
		std::getline(ss, duration, ':');
		std::cout << line << std::endl;
		if (name.size() > 0 && name[0] != '#') {
			Step step;
			step.presetName = name;
			step.delta = std::stof(delta);
			step.duration = std::stof(duration);
			steps.push(step);
			// std::cout << name  << ":" << delta << ":" << duration << std::endl;
		}
	}
	if (f.bad()) {
		std::cout << "Error reading:" << sequenceName << std::endl;
	}
	return steps;
}

void PresetSequencer::registerBeginCallback(std::function<void(PresetSequencer *sender, void *userData)> beginCallback,
                                            void *userData)
{
	mBeginCallback = beginCallback;
	mBeginCallbackData = userData;
	mBeginCallbackEnabled = true;
}

void PresetSequencer::registerEndCallback(std::function<void (bool, al::PresetSequencer *, void *)> endCallback,
                                          void *userData)
{
	// FIXME this data needs to be protected with a mutex
	mEndCallback = endCallback;
	mEndCallbackData = userData;
	mEndCallbackEnabled = true;
}

float PresetSequencer::getSequenceTotalDuration(std::string sequenceName)
{
	std::queue<Step> steps = loadSequence(sequenceName);
	float duration = 0.0f;
	while (steps.size() > 0) {
		const Step &step = steps.front();
		duration += step.delta + step.duration;
		steps.pop();
	}
	return duration;
}

void PresetSequencer::clearSteps()
{
	stopSequence();
	mSequenceLock.lock();
	while (!mSteps.empty()) {
		mSteps.pop();
	}
	mSequenceLock.unlock();
}

void PresetSequencer::appendStep(PresetSequencer::Step &newStep)
{
	mSequenceLock.lock();
	mSteps.push(newStep);
	mSequenceLock.unlock();
}

bool PresetSequencer::consumeMessage(osc::Message &m, std::string rootOSCPath)
{
	std::string basePath = rootOSCPath;
	if (mOSCsubPath.size() > 0) {
		basePath += "/" + mOSCsubPath;
	}
	if(m.addressPattern() == basePath && m.typeTags() == "s"){
		std::string val;
		m >> val;
		std::cout << "start sequence " << val << std::endl;
		playSequence(val);
		return true;
	} else if(m.addressPattern() == basePath + "/stop" ){
		std::cout << "stop sequence " << std::endl;
		stopSequence();
		return true;
	}
	return false;
}

std::string PresetSequencer::buildFullPath(std::string sequenceName)
{
	std::string fullName = mDirectory;
	if (mPresetHandler) {
		fullName = mPresetHandler->getCurrentPath();
	}
	if (fullName.back() != '/') {
		fullName += "/";
	}
	if (sequenceName.size() < 9 || sequenceName.substr(sequenceName.size() - 9) != ".sequence") {
		fullName += sequenceName + ".sequence";
	}
	return fullName;
}

// SequenceServer ----------------------------------------------------------------

SequenceServer::SequenceServer(std::string oscAddress, int oscPort) :
    mServer(nullptr), mRecorder(nullptr),
    mParamServer(nullptr),
    mOSCpath("/sequence")
{
	mServer = new osc::Recv(oscPort, oscAddress.c_str(), 0.001); // Is this 1ms wait OK?
	if (mServer) {
		mServer->handler(*this);
		mServer->start();
	} else {
		std::cout << "Error starting OSC server." << std::endl;
	}
}

SequenceServer::SequenceServer(ParameterServer &paramServer) :
    mServer(nullptr),
    mParamServer(&paramServer),
    mOSCpath("/sequence")
{
	paramServer.registerOSCListener(this);
}

SequenceServer::~SequenceServer()
{
//	std::cout << "~SequenceServer()" << std::endl;;
	if (mServer) {
		mServer->stop();
		delete mServer;
		mServer = nullptr;
	}
}

void SequenceServer::onMessage(osc::Message &m)
{
	if(m.addressPattern() == mOSCpath + "/last"){
		if (mSequencer && mRecorder) {
			std::cout << "start last recorder sequence " << mRecorder->lastSequenceName() << std::endl;
			mSequencer->setHandlerSubDirectory(mRecorder->lastSequenceSubDir());
			mSequencer->playSequence( mRecorder->lastSequenceName());
		} else {
			std::cerr << "SequenceRecorder and PresetSequencer must be registered to enable /*/last." << std::endl;
		}
	} else {
		for(osc::MessageConsumer *consumer: mConsumers) {
			if (consumer->consumeMessage(m, mOSCpath)) {
				break;
			}
		}
	}
}

SequenceServer &SequenceServer::registerMessageConsumer(osc::MessageConsumer &consumer) {
	mConsumers.push_back(&consumer);
	return *this;
}

SequenceServer &SequenceServer::registerRecorder(SequenceRecorder &recorder) {
	mRecorder = &recorder;
	mConsumers.push_back(static_cast<osc::MessageConsumer *>(&recorder));
	return *this;
}

SequenceServer &SequenceServer::registerSequencer(PresetSequencer &sequencer) {
	mSequencer = &sequencer;
	mConsumers.push_back(&sequencer);
	return *this;
}

void SequenceServer::print()
{
	if (mServer) {
		std::cout << "Sequence server listening on: " << mServer->address() << ":" << mServer->port() << std::endl;
		std::cout << "Communicating on path: " << mOSCpath << std::endl;
	}
	for (auto sender:mOSCSenders) {
		std::cout << sender->address() << ":" << sender->port() << std::endl;
	}
}

void SequenceServer::stopServer()
{
	if (mServer) {
		mServer->stop();
		delete mServer;
		mServer = nullptr;
	}
}

void SequenceServer::setAddress(std::string address)
{
	mOSCpath = address;
}

std::string SequenceServer::getAddress()
{
	return mOSCpath;
}

void SequenceServer::changeCallback(int value, void *sender, void *userData)
{
	SequenceServer *server = static_cast<SequenceServer *>(userData);
	Parameter *parameter = static_cast<Parameter *>(sender);
	server->notifyListeners(server->mOSCpath, value);
}

<|endoftext|>


<commit_before>#include "ofSoundStream.h"
#include "RtAudio.h"

//----------------------------------- static variables:
static ofBaseApp 	* 		OFSAptr;
RtAudio				*		audio;
int 						nInputChannels;
int 						nOutputChannels;
ofAudioEventArgs 			audioEventArgs;
int 	receiveAudioBufferAndCallSimpleApp(char *buffer, int bufferSize, void *data);

//------------------------------------------------------------------------------
int receiveAudioBufferAndCallSimpleApp(char *buffer, int bufferSize, void *data){
	// 	rtAudio uses a system by which the audio
	// 	can be of different formats
	// 	char, float, etc.
	// 	we choose float

	float * fPtr = (float *)buffer;

	// [zach] memset output to zero before output call
	// this is because of how rtAudio works: duplex w/ one callback
	// you need to cut in the middle. if the simpleApp
	// doesn't produce audio, we pass silence instead of duplex...

	if (nInputChannels > 0){
		if(OFSAptr)
			OFSAptr->audioReceived(fPtr, bufferSize, nInputChannels);

		#ifdef OF_CORE_EVENTS_ENABLED
			audioEventArgs.buffer = fPtr;
			audioEventArgs.bufferSize = bufferSize;
			audioEventArgs.nChannels = nInputChannels;
			ofNotifyEvent( ofEvents.audioReceived, audioEventArgs );
		#endif

		memset(fPtr, 0, bufferSize * nInputChannels * sizeof(float));
	}
	if (nOutputChannels > 0){
		if(OFSAptr)
			OFSAptr->audioRequested(fPtr, bufferSize, nOutputChannels);

		#ifdef OF_CORE_EVENTS_ENABLED
			audioEventArgs.buffer = fPtr;
			audioEventArgs.bufferSize = bufferSize;
			audioEventArgs.nChannels = nOutputChannels;
			ofNotifyEvent( ofEvents.audioRequested, audioEventArgs );
		#endif

    }

	return 0;
}

//---------------------------------------------------------
void ofSoundStreamSetup(int nOutputs, int nInputs, ofBaseApp * OFSA){
	ofSoundStreamSetup(nOutputs, nInputs, OFSA, 44100, 256, 4);
}

//---------------------------------------------------------
void ofSoundStreamSetup(int nOutputs, int nInputs, int sampleRate, int bufferSize, int nBuffers){
	ofSoundStreamSetup(nOutputs, nInputs, NULL, sampleRate, bufferSize, nBuffers);
}

//---------------------------------------------------------
void ofSoundStreamSetup(int nOutputs, int nInputs, ofBaseApp * OFSA, int sampleRate, int bufferSize, int nBuffers){

	nInputChannels 		=  nInputs;
	nOutputChannels 	=  nOutputs;
	int device 			=  0;        // default
	OFSAptr 			=  OFSA;

	bufferSize = ofNextPow2(bufferSize);	// must be pow2

	try {
		audio = new RtAudio();
		audio->openStream(	device, nOutputs, device, nInputs, RTAUDIO_FLOAT32,
                      		sampleRate, &bufferSize, nBuffers);
	} catch (RtError &error) {
		error.printMessage();
		//std::exit(EXIT_FAILURE); // need case here
	}

	try {
		audio->setStreamCallback(&receiveAudioBufferAndCallSimpleApp, (void *)NULL);
		audio->startStream();
	} catch (RtError &error) {
		error.printMessage();
	}
}

//---------------------------------------------------------
void ofSoundStreamStop(){
	try {
    	audio->stopStream();
  	} catch (RtError &error) {
   		error.printMessage();
 	}
}

//---------------------------------------------------------
void ofSoundStreamStart(){
	try{
		audio->startStream();
	} catch (RtError &error) {
		error.printMessage();
	}
}

//---------------------------------------------------------
void ofSoundStreamClose(){
	try {
    	audio->stopStream();
    	audio->closeStream();
  	} catch (RtError &error) {
   		error.printMessage();
 	}
	delete audio;
}

//---------------------------------------------------------
void ofSoundStreamListDevices(){
	RtAudio *audioTemp = 0;
	try {
		audioTemp = new RtAudio();
	} catch (RtError &error) {
		error.printMessage();
	}
 	int devices = audioTemp->getDeviceCount();
	RtAudioDeviceInfo info;
	for (int i=1; i<=devices; i++) {
		try {
			info = audioTemp->getDeviceInfo(i);
		} catch (RtError &error) {
			error.printMessage();
			break;
		}
		std::cout << "device = " << i << " (" << info.name << ")\n";
		if (info.isDefault) std::cout << "----* default ----* \n";
		std::cout << "maximum output channels = " << info.outputChannels << "\n";
		std::cout << "maximum input channels = " << info.inputChannels << "\n";
		std::cout << "-----------------------------------------\n";

	}
	delete audioTemp;
}

<commit_msg>events weren't working because of wrong ifdef<commit_after>#include "ofSoundStream.h"
#include "RtAudio.h"

//----------------------------------- static variables:
static ofBaseApp 	* 		OFSAptr;
RtAudio				*		audio;
int 						nInputChannels;
int 						nOutputChannels;
ofAudioEventArgs 			audioEventArgs;
int 	receiveAudioBufferAndCallSimpleApp(char *buffer, int bufferSize, void *data);

//------------------------------------------------------------------------------
int receiveAudioBufferAndCallSimpleApp(char *buffer, int bufferSize, void *data){
	// 	rtAudio uses a system by which the audio
	// 	can be of different formats
	// 	char, float, etc.
	// 	we choose float

	float * fPtr = (float *)buffer;

	// [zach] memset output to zero before output call
	// this is because of how rtAudio works: duplex w/ one callback
	// you need to cut in the middle. if the simpleApp
	// doesn't produce audio, we pass silence instead of duplex...

	if (nInputChannels > 0){
		if(OFSAptr)
			OFSAptr->audioReceived(fPtr, bufferSize, nInputChannels);

		#ifdef OF_USING_POCO
			audioEventArgs.buffer = fPtr;
			audioEventArgs.bufferSize = bufferSize;
			audioEventArgs.nChannels = nInputChannels;
			ofNotifyEvent( ofEvents.audioReceived, audioEventArgs );
		#endif

		memset(fPtr, 0, bufferSize * nInputChannels * sizeof(float));
	}
	if (nOutputChannels > 0){
		if(OFSAptr)
			OFSAptr->audioRequested(fPtr, bufferSize, nOutputChannels);

		#ifdef OF_USING_POCO
			audioEventArgs.buffer = fPtr;
			audioEventArgs.bufferSize = bufferSize;
			audioEventArgs.nChannels = nOutputChannels;
			ofNotifyEvent( ofEvents.audioRequested, audioEventArgs );
		#endif

    }

	return 0;
}

//---------------------------------------------------------
void ofSoundStreamSetup(int nOutputs, int nInputs, ofBaseApp * OFSA){
	ofSoundStreamSetup(nOutputs, nInputs, OFSA, 44100, 256, 4);
}

//---------------------------------------------------------
void ofSoundStreamSetup(int nOutputs, int nInputs, int sampleRate, int bufferSize, int nBuffers){
	ofSoundStreamSetup(nOutputs, nInputs, NULL, sampleRate, bufferSize, nBuffers);
}

//---------------------------------------------------------
void ofSoundStreamSetup(int nOutputs, int nInputs, ofBaseApp * OFSA, int sampleRate, int bufferSize, int nBuffers){

	nInputChannels 		=  nInputs;
	nOutputChannels 	=  nOutputs;
	int device 			=  0;        // default
	OFSAptr 			=  OFSA;

	bufferSize = ofNextPow2(bufferSize);	// must be pow2

	try {
		audio = new RtAudio();
		audio->openStream(	device, nOutputs, device, nInputs, RTAUDIO_FLOAT32,
                      		sampleRate, &bufferSize, nBuffers);
	} catch (RtError &error) {
		error.printMessage();
		//std::exit(EXIT_FAILURE); // need case here
	}

	try {
		audio->setStreamCallback(&receiveAudioBufferAndCallSimpleApp, (void *)NULL);
		audio->startStream();
	} catch (RtError &error) {
		error.printMessage();
	}
}

//---------------------------------------------------------
void ofSoundStreamStop(){
	try {
    	audio->stopStream();
  	} catch (RtError &error) {
   		error.printMessage();
 	}
}

//---------------------------------------------------------
void ofSoundStreamStart(){
	try{
		audio->startStream();
	} catch (RtError &error) {
		error.printMessage();
	}
}

//---------------------------------------------------------
void ofSoundStreamClose(){
	try {
    	audio->stopStream();
    	audio->closeStream();
  	} catch (RtError &error) {
   		error.printMessage();
 	}
	delete audio;
}

//---------------------------------------------------------
void ofSoundStreamListDevices(){
	RtAudio *audioTemp = 0;
	try {
		audioTemp = new RtAudio();
	} catch (RtError &error) {
		error.printMessage();
	}
 	int devices = audioTemp->getDeviceCount();
	RtAudioDeviceInfo info;
	for (int i=1; i<=devices; i++) {
		try {
			info = audioTemp->getDeviceInfo(i);
		} catch (RtError &error) {
			error.printMessage();
			break;
		}
		std::cout << "device = " << i << " (" << info.name << ")\n";
		if (info.isDefault) std::cout << "----* default ----* \n";
		std::cout << "maximum output channels = " << info.outputChannels << "\n";
		std::cout << "maximum input channels = " << info.inputChannels << "\n";
		std::cout << "-----------------------------------------\n";

	}
	delete audioTemp;
}

<|endoftext|>