<commit_before>#include "gtest/gtest.h"
#include <boost/thread.hpp>
#include <boost/timer.hpp>
#include <vector>
#include <list>
#include <atomic>
#include "coroutine.h"
#include "gtest_exit.h"
using namespace std::chrono;
using namespace co;

//#define EXPECT_YIELD(n) EXPECT_EQ(g_Scheduler.GetCurrentTaskYieldCount(), size_t(n))
#define EXPECT_YIELD(n)

#define SLEEP(ms) \
    do {\
        Processer::Suspend(milliseconds(ms)); co_yield;\
    } while(0)

TEST(Channel, capacity0)
{
    co_chan<int> ch;
    EXPECT_TRUE(ch.empty());

    int i = 0;
    {
        EXPECT_EQ(ch.size(), 0u);
        go [&]{
            ch << 1;
            EXPECT_YIELD(1);
        };
        go [&]{
            ch >> i;
            EXPECT_YIELD(0);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

//    co_opt.debug = co::dbg_channel;
//    co_opt.debug_output = fopen("log", "w");

        EXPECT_EQ(ch.size(), 0u);
        go [=]{ ch << 2; EXPECT_YIELD(1);};
        go [=, &i]{ ch >> i; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);

//    exit(0);

        EXPECT_EQ(ch.size(), 0u);
        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch << 3; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ before_pop = ++step; ch >> i; after_pop = ++step; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 3);
        EXPECT_EQ(before_push, 1);
        EXPECT_EQ(before_pop, 2);
        EXPECT_EQ(ch.size(), 0u);
    }

    // ignore
    {
        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch << 3; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ before_pop = ++step; ch >> nullptr; after_pop = ++step; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(before_push, 1);
        EXPECT_EQ(before_pop, 2);
    }

    // multi thread
    int k = 0;
    std::atomic<int> total{0};
    int total_check = 0;
    for (k = 0; k < 100; ++k) {
        total_check += k;
        go [=]{
            ch << k;
        };
        go [&]{
            int v;
            ch >> v;
            total += v;
        };
    }
    WaitUntilNoTask();
    EXPECT_EQ(total, total_check);
}

TEST(Channel, capacity1)
{
    // nonblock
    co_chan<int> ch(1);
    EXPECT_TRUE(ch.empty());
    int i = -1;

    {
        EXPECT_EQ(ch.size(), 0u);
        go [&]{
            ch << 1;
            EXPECT_FALSE(ch.empty());
            EXPECT_EQ(ch.size(), 1u);
            EXPECT_YIELD(0);
            go [&]{
                ch >> i;
                EXPECT_YIELD(0);
            };
        };
        WaitUntilNoTask();
        EXPECT_EQ(ch.size(), 0u);
        EXPECT_EQ(i, 1);

        go [=]{ ch << 2; EXPECT_YIELD(0);};
        go [=, &i]{ SLEEP(50); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    {
        // block
        ch << 0;
        go [&]{ ch << 1; EXPECT_YIELD(1);};
        go [&]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 0);

        go [=]{ ch << 2; EXPECT_YIELD(1);};
        go [=, &i]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch << 3; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); before_pop = ++step; ch >> i; after_pop = ++step; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
        EXPECT_EQ(before_push + before_pop, 3);
        EXPECT_EQ(after_pop + after_push, 7);

        EXPECT_TRUE(ch.TryPop(i));
        EXPECT_EQ(i, 3);
    }
}

TEST(Channel, capacityN)
{
    int n = 10;
    co_chan<int> ch(n);
    int i = 0;

    // nonblock
    {
        go [&]{ ch << 1; EXPECT_YIELD(0);};
        go [&]{ ch >> i; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [=]{ ch << 2; EXPECT_YIELD(0);};
        go [=, &i]{ ch >> i; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    // block
    {
        for (int i = 0; i < n; ++i)
            ch << i;
        go [&]{ ch << n; EXPECT_YIELD(1);};
        go [&]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 0);

        go [=]{ ch << n + 1; EXPECT_YIELD(1);};
        go [=, &i]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        for (int i = 0; i < n; ++i) {
            int x;
            ch >> x;
            EXPECT_EQ(x, i + 2);
        }
    }
}

TEST(Channel, capacity0Try)
{
    co_chan<int> ch;
    int i = 0;
    // try pop
    if (0)
    {
        go [&]{ EXPECT_FALSE(ch.TryPop(i)); EXPECT_YIELD(0); SLEEP(100); EXPECT_TRUE(ch.TryPop(i)); EXPECT_YIELD(1); };
        go [&]{ SLEEP(50); ch << 1; EXPECT_YIELD(2);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [=]{ ch << 2; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); EXPECT_TRUE(ch.TryPop(i)); EXPECT_YIELD(1); };
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    // try push
    {
        go [&]{
            EXPECT_FALSE(ch.TryPush(1));
            go [&] {
                SLEEP(50);
                EXPECT_TRUE(ch.TryPush(1));
            };
            ch >> i;
            EXPECT_YIELD(1);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [&]{ ch >> i; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); EXPECT_TRUE(ch.TryPush(2)); EXPECT_YIELD(1); };
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);

        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch >> i; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); before_pop = ++step; EXPECT_TRUE(ch.TryPush(3)); after_pop = ++step; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 3);
        EXPECT_EQ(before_push, 1);
        EXPECT_EQ(before_pop, 2);
        EXPECT_EQ(after_pop, 3);
        EXPECT_EQ(after_push, 4);
    }

    // try push and pop
    {
        go [&]{
            for (int i = 0; i < 10000; ++i) {
                EXPECT_FALSE(ch.TryPush(1));
            }
        };
        go [&]{
            int j;
            for (int i = 0; i < 10000; ++i) {
                EXPECT_FALSE(ch.TryPop(j));
            }
        };
        WaitUntilNoTask();
    }
}

TEST(Channel, capacity1Try)
{
    co_chan<int> ch(1);
    int i = 0;
    // try pop
    {
        go [&]{
            EXPECT_FALSE(ch.TryPop(i));
            EXPECT_YIELD(0);
            SLEEP(100);
            EXPECT_TRUE(ch.TryPop(i));
            EXPECT_YIELD(1);
        };
        go [&]{
            SLEEP(50);
            ch << 1;
            EXPECT_YIELD(1);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [=]{
            ch << 2;
            EXPECT_YIELD(0);
        };
        go [&]{
            SLEEP(50);
            EXPECT_TRUE(ch.TryPop(i));
            EXPECT_YIELD(1);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    // try push
    {
        ch << 0;
        go [&]{
            EXPECT_FALSE(ch.TryPush(1));
            EXPECT_YIELD(0);
            SLEEP(100);
            EXPECT_TRUE(ch.TryPush(1));
            EXPECT_YIELD(1);
        };

        go [&]{ SLEEP(50);
            ch >> i;
            EXPECT_YIELD(1);
        };

        WaitUntilNoTask();
        EXPECT_EQ(i, 0);

        go [&]{ ch >> i;
            EXPECT_YIELD(0);
        };

        go [&]{ SLEEP(50);
            EXPECT_TRUE(ch.TryPush(2));
            EXPECT_YIELD(1);
        };

        WaitUntilNoTask();
        EXPECT_EQ(i, 1);
    }
}

TEST(Channel, capacity0TimedTypes)
{
    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, seconds(1));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 1000, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, milliseconds(32));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 32, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, std::chrono::milliseconds(32));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 32, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }
}

TEST(Channel, capacity0Timed)
{
    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, milliseconds(32));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 32, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        go [=] {
            GTimer t;
            int i;
            bool ok = ch.TimedPop(i, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        go [=] {
            GTimer t;
            bool ok = ch.TimedPop(nullptr, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<void> ch;

        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(nullptr, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        go [=] {
            GTimer t;
            bool ok = ch.TimedPop(nullptr, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<void> ch;

        for (int i = 0; i < 1000; ++i)
            go [=] {
                GTimer t;
                bool ok = ch.TimedPush(nullptr, milliseconds(500));
                EXPECT_FALSE(ok);
                TIMER_CHECK(t, 500, 50);
            };
        WaitUntilNoTask();
    }

    {
        co_chan<void> ch;

        for (int i = 0; i < 1000; ++i)
            go [=] {
                GTimer t;
                bool ok = ch.TimedPop(nullptr, milliseconds(500));
                EXPECT_FALSE(ok);
                TIMER_CHECK(t, 500, 50);
            };
        WaitUntilNoTask();
    }

    {
        co_chan<int> ch;
        int *p = new int[1000];

        for (int i = 0; i < 1000; ++i)
            go [=] {
                int v;
                GTimer t;
                bool ok = ch.TimedPop(v, milliseconds(500));
                EXPECT_TRUE(ok);
                p[v] = 1;
                TIMER_CHECK(t, 0, 200);
            };

        for (int i = 0; i < 1000; ++i)
            go [=] {
                ch << i;
            };

        WaitUntilNoTask();

        for (int i = 0; i < 1000; ++i) {
            EXPECT_EQ(p[i], 1);
        }
        delete[] p;
    }
}
<commit_msg>兼容性能较差的ci虚拟机<commit_after>#include "gtest/gtest.h"
#include <boost/thread.hpp>
#include <boost/timer.hpp>
#include <vector>
#include <list>
#include <atomic>
#include "coroutine.h"
#include "gtest_exit.h"
using namespace std::chrono;
using namespace co;

//#define EXPECT_YIELD(n) EXPECT_EQ(g_Scheduler.GetCurrentTaskYieldCount(), size_t(n))
#define EXPECT_YIELD(n)

#define SLEEP(ms) \
    do {\
        Processer::Suspend(milliseconds(ms)); co_yield;\
    } while(0)

TEST(Channel, capacity0)
{
    co_chan<int> ch;
    EXPECT_TRUE(ch.empty());

    int i = 0;
    {
        EXPECT_EQ(ch.size(), 0u);
        go [&]{
            ch << 1;
            EXPECT_YIELD(1);
        };
        go [&]{
            ch >> i;
            EXPECT_YIELD(0);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

//    co_opt.debug = co::dbg_channel;
//    co_opt.debug_output = fopen("log", "w");

        EXPECT_EQ(ch.size(), 0u);
        go [=]{ ch << 2; EXPECT_YIELD(1);};
        go [=, &i]{ ch >> i; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);

//    exit(0);

        EXPECT_EQ(ch.size(), 0u);
        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch << 3; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ before_pop = ++step; ch >> i; after_pop = ++step; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 3);
        EXPECT_EQ(before_push, 1);
        EXPECT_EQ(before_pop, 2);
        EXPECT_EQ(ch.size(), 0u);
    }

    // ignore
    {
        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch << 3; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ before_pop = ++step; ch >> nullptr; after_pop = ++step; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(before_push, 1);
        EXPECT_EQ(before_pop, 2);
    }

    // multi thread
    int k = 0;
    std::atomic<int> total{0};
    int total_check = 0;
    for (k = 0; k < 100; ++k) {
        total_check += k;
        go [=]{
            ch << k;
        };
        go [&]{
            int v;
            ch >> v;
            total += v;
        };
    }
    WaitUntilNoTask();
    EXPECT_EQ(total, total_check);
}

TEST(Channel, capacity1)
{
    // nonblock
    co_chan<int> ch(1);
    EXPECT_TRUE(ch.empty());
    int i = -1;

    {
        EXPECT_EQ(ch.size(), 0u);
        go [&]{
            ch << 1;
            EXPECT_FALSE(ch.empty());
            EXPECT_EQ(ch.size(), 1u);
            EXPECT_YIELD(0);
            go [&]{
                ch >> i;
                EXPECT_YIELD(0);
            };
        };
        WaitUntilNoTask();
        EXPECT_EQ(ch.size(), 0u);
        EXPECT_EQ(i, 1);

        go [=]{ ch << 2; EXPECT_YIELD(0);};
        go [=, &i]{ SLEEP(50); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    {
        // block
        ch << 0;
        go [&]{ ch << 1; EXPECT_YIELD(1);};
        go [&]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 0);

        go [=]{ ch << 2; EXPECT_YIELD(1);};
        go [=, &i]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch << 3; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); before_pop = ++step; ch >> i; after_pop = ++step; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
        EXPECT_EQ(before_push + before_pop, 3);
        EXPECT_EQ(after_pop + after_push, 7);

        EXPECT_TRUE(ch.TryPop(i));
        EXPECT_EQ(i, 3);
    }
}

TEST(Channel, capacityN)
{
    int n = 10;
    co_chan<int> ch(n);
    int i = 0;

    // nonblock
    {
        go [&]{ ch << 1; EXPECT_YIELD(0);};
        go [&]{ ch >> i; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [=]{ ch << 2; EXPECT_YIELD(0);};
        go [=, &i]{ ch >> i; EXPECT_YIELD(0);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    // block
    {
        for (int i = 0; i < n; ++i)
            ch << i;
        go [&]{ ch << n; EXPECT_YIELD(1);};
        go [&]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 0);

        go [=]{ ch << n + 1; EXPECT_YIELD(1);};
        go [=, &i]{ SLEEP(100); ch >> i; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        for (int i = 0; i < n; ++i) {
            int x;
            ch >> x;
            EXPECT_EQ(x, i + 2);
        }
    }
}

TEST(Channel, capacity0Try)
{
    co_chan<int> ch;
    int i = 0;
    // try pop
    if (0)
    {
        go [&]{ EXPECT_FALSE(ch.TryPop(i)); EXPECT_YIELD(0); SLEEP(100); EXPECT_TRUE(ch.TryPop(i)); EXPECT_YIELD(1); };
        go [&]{ SLEEP(50); ch << 1; EXPECT_YIELD(2);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [=]{ ch << 2; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); EXPECT_TRUE(ch.TryPop(i)); EXPECT_YIELD(1); };
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    // try push
    {
        go [&]{
            EXPECT_FALSE(ch.TryPush(1));
            go [&] {
                SLEEP(50);
                EXPECT_TRUE(ch.TryPush(1));
            };
            ch >> i;
            EXPECT_YIELD(1);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [&]{ ch >> i; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); EXPECT_TRUE(ch.TryPush(2)); EXPECT_YIELD(1); };
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);

        std::atomic<int> step{0};
        int before_push = 0, after_push = 0, before_pop = 0, after_pop = 0;
        go [&]{ before_push = ++step; ch >> i; after_push = ++step; EXPECT_YIELD(1);};
        go [&]{ SLEEP(50); before_pop = ++step; EXPECT_TRUE(ch.TryPush(3)); after_pop = ++step; EXPECT_YIELD(1);};
        WaitUntilNoTask();
        EXPECT_EQ(i, 3);
        EXPECT_EQ(before_push, 1);
        EXPECT_EQ(before_pop, 2);
        EXPECT_EQ(after_pop, 3);
        EXPECT_EQ(after_push, 4);
    }

    // try push and pop
    {
        go [&]{
            for (int i = 0; i < 10000; ++i) {
                EXPECT_FALSE(ch.TryPush(1));
            }
        };
        go [&]{
            int j;
            for (int i = 0; i < 10000; ++i) {
                EXPECT_FALSE(ch.TryPop(j));
            }
        };
        WaitUntilNoTask();
    }
}

TEST(Channel, capacity1Try)
{
    co_chan<int> ch(1);
    int i = 0;
    // try pop
    {
        go [&]{
            EXPECT_FALSE(ch.TryPop(i));
            EXPECT_YIELD(0);
            SLEEP(100);
            EXPECT_TRUE(ch.TryPop(i));
            EXPECT_YIELD(1);
        };
        go [&]{
            SLEEP(50);
            ch << 1;
            EXPECT_YIELD(1);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 1);

        go [=]{
            ch << 2;
            EXPECT_YIELD(0);
        };
        go [&]{
            SLEEP(50);
            EXPECT_TRUE(ch.TryPop(i));
            EXPECT_YIELD(1);
        };
        WaitUntilNoTask();
        EXPECT_EQ(i, 2);
    }

    // try push
    {
        ch << 0;
        go [&]{
            EXPECT_FALSE(ch.TryPush(1));
            EXPECT_YIELD(0);
            SLEEP(100);
            EXPECT_TRUE(ch.TryPush(1));
            EXPECT_YIELD(1);
        };

        go [&]{ SLEEP(50);
            ch >> i;
            EXPECT_YIELD(1);
        };

        WaitUntilNoTask();
        EXPECT_EQ(i, 0);

        go [&]{ ch >> i;
            EXPECT_YIELD(0);
        };

        go [&]{ SLEEP(50);
            EXPECT_TRUE(ch.TryPush(2));
            EXPECT_YIELD(1);
        };

        WaitUntilNoTask();
        EXPECT_EQ(i, 1);
    }
}

TEST(Channel, capacity0TimedTypes)
{
    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, seconds(1));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 1000, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, milliseconds(32));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 32, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, std::chrono::milliseconds(32));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 32, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }
}

TEST(Channel, capacity0Timed)
{
    {
        co_chan<int> ch;

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, milliseconds(32));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 32, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        // block try
        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(1, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        go [=] {
            GTimer t;
            int i;
            bool ok = ch.TimedPop(i, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        go [=] {
            GTimer t;
            bool ok = ch.TimedPop(nullptr, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<void> ch;

        go [=] {
            GTimer t;
            bool ok = ch.TimedPush(nullptr, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();

        go [=] {
            GTimer t;
            bool ok = ch.TimedPop(nullptr, milliseconds(100));
            EXPECT_FALSE(ok);
            TIMER_CHECK(t, 100, DEFAULT_DEVIATION);
        };
        WaitUntilNoTask();
    }

    {
        co_chan<void> ch;

        for (int i = 0; i < 1000; ++i)
            go [=] {
                GTimer t;
                bool ok = ch.TimedPush(nullptr, milliseconds(500));
                EXPECT_FALSE(ok);
                TIMER_CHECK(t, 500, 100);
            };
        WaitUntilNoTask();
    }

    {
        co_chan<void> ch;

        for (int i = 0; i < 1000; ++i)
            go [=] {
                GTimer t;
                bool ok = ch.TimedPop(nullptr, milliseconds(500));
                EXPECT_FALSE(ok);
                TIMER_CHECK(t, 500, 100);
            };
        WaitUntilNoTask();
    }

    {
        co_chan<int> ch;
        int *p = new int[1000];

        for (int i = 0; i < 1000; ++i)
            go [=] {
                int v;
                GTimer t;
                bool ok = ch.TimedPop(v, milliseconds(500));
                EXPECT_TRUE(ok);
                p[v] = 1;
                TIMER_CHECK(t, 0, 200);
            };

        for (int i = 0; i < 1000; ++i)
            go [=] {
                ch << i;
            };

        WaitUntilNoTask();

        for (int i = 0; i < 1000; ++i) {
            EXPECT_EQ(p[i], 1);
        }
        delete[] p;
    }
}
<|endoftext|>


<commit_before>//===------------------------- incomplete_type.cpp --------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// http://mentorembedded.github.io/cxx-abi/abi.html#rtti-layout

// Two abi::__pbase_type_info objects can always be compared for equality
// (i.e. of the types represented) or ordering by comparison of their name
// NTBS addresses. In addition, unless either or both have either of the
// incomplete flags set, equality can be tested by comparing the type_info
// addresses.

// UNSUPPORTED: libcxxabi-no-exceptions

// RUN: %cxx %flags %compile_flags -c %s -o %t.one.o
// RUN: %cxx %flags %compile_flags -c %s -o %t.two.o -DTU_ONE
// RUN: %cxx %flags %t.one.o %t.two.o %link_flags -o %t.exe
// RUN: %t.exe

#include <stdio.h>
#include <cstring>
#include <cassert>
#include <typeinfo>

// Check that the addresses of the typeinfo differ but still compare equal
// via their NTBS.
inline void
AssertIncompleteTypeInfoEquals(std::type_info const& LHS, std::type_info const& RHS)
{
  assert(&LHS != &RHS);
  assert(strcmp(LHS.name(), RHS.name()) == 0);
}

struct NeverDefined;
void ThrowNeverDefinedMP();
std::type_info const& ReturnTypeInfoNeverDefinedMP();

struct IncompleteAtThrow;
void ThrowIncompleteMP();
void ThrowIncompletePP();
void ThrowIncompletePMP();
std::type_info const& ReturnTypeInfoIncompleteMP();
std::type_info const& ReturnTypeInfoIncompletePP();

struct CompleteAtThrow;
void ThrowCompleteMP();
void ThrowCompletePP();
void ThrowCompletePMP();
std::type_info const& ReturnTypeInfoCompleteMP();
std::type_info const& ReturnTypeInfoCompletePP();

void ThrowNullptr();

#ifndef TU_ONE

void ThrowNeverDefinedMP() { throw (int NeverDefined::*)nullptr; }
std::type_info const& ReturnTypeInfoNeverDefinedMP() { return typeid(int NeverDefined::*); }

void ThrowIncompleteMP() { throw (int IncompleteAtThrow::*)nullptr; }
void ThrowIncompletePP() { throw (IncompleteAtThrow**)nullptr; }
void ThrowIncompletePMP() { throw (int IncompleteAtThrow::**)nullptr; }
std::type_info const& ReturnTypeInfoIncompleteMP() { return typeid(int IncompleteAtThrow::*); }
std::type_info const& ReturnTypeInfoIncompletePP() { return typeid(IncompleteAtThrow**); }

struct CompleteAtThrow {};
void ThrowCompleteMP() { throw (int CompleteAtThrow::*)nullptr; }
void ThrowCompletePP() { throw (CompleteAtThrow**)nullptr; }
void ThrowCompletePMP() { throw (int CompleteAtThrow::**)nullptr; }
std::type_info const& ReturnTypeInfoCompleteMP() { return typeid(int CompleteAtThrow::*); }
std::type_info const& ReturnTypeInfoCompletePP() { return typeid(CompleteAtThrow**); }

void ThrowNullptr() { throw nullptr; }

#else

struct IncompleteAtThrow {};

int main() {
  AssertIncompleteTypeInfoEquals(ReturnTypeInfoNeverDefinedMP(), typeid(int NeverDefined::*));
  try {
    ThrowNeverDefinedMP();
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (int NeverDefined::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch NeverDefined::*" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompleteMP(), typeid(int IncompleteAtThrow::*));
  try {
    ThrowIncompleteMP();
    assert(false);
  } catch (CompleteAtThrow**) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow::*" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompletePP(), typeid(IncompleteAtThrow**));
  try {
    ThrowIncompletePP();
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (IncompleteAtThrow** p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow**" ); }

  try {
    ThrowIncompletePMP();
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::**p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow::**" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompleteMP(), typeid(int CompleteAtThrow::*));
  try {
    ThrowCompleteMP();
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (CompleteAtThrow**) {
    assert(false);
  } catch (int CompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow::" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompletePP(), typeid(CompleteAtThrow**));
  try {
    ThrowCompletePP();
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (CompleteAtThrow**p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow**" ); }

  try {
    ThrowCompletePMP();
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (CompleteAtThrow**) {
    assert(false);
  } catch (int CompleteAtThrow::**p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow::**" ); }

#if __cplusplus >= 201103L
  // Catch nullptr as complete type
  try {
    ThrowNullptr();
  } catch (int IncompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch nullptr as IncompleteAtThrow::*" ); }

  // Catch nullptr as an incomplete type
  try {
    ThrowNullptr();
  } catch (int CompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch nullptr as CompleteAtThrow::*" ); }

  // Catch nullptr as a type that is never complete.
  try {
    ThrowNullptr();
  } catch (int NeverDefined::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch nullptr as NeverDefined::*" ); }

#endif
}
#endif
<commit_msg>Fix incomplete type test on OS X; workaround weird DYLD_LIBRARY_PATH behavior<commit_after>//===------------------------- incomplete_type.cpp --------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// http://mentorembedded.github.io/cxx-abi/abi.html#rtti-layout

// Two abi::__pbase_type_info objects can always be compared for equality
// (i.e. of the types represented) or ordering by comparison of their name
// NTBS addresses. In addition, unless either or both have either of the
// incomplete flags set, equality can be tested by comparing the type_info
// addresses.

// UNSUPPORTED: libcxxabi-no-exceptions

// NOTE: Pass -lc++abi explicitly and before -lc++ so that -lc++ doesn't drag
// in the system libc++abi installation on OS X. (DYLD_LIBRARY_PATH is ignored
// for shell tests because of Apple security features).

// RUN: %cxx %flags %compile_flags -c %s -o %t.one.o
// RUN: %cxx %flags %compile_flags -c %s -o %t.two.o -DTU_ONE
// RUN: %cxx %flags %t.one.o %t.two.o -lc++abi %link_flags -o %t.exe
// RUN: %t.exe

#include <stdio.h>
#include <cstring>
#include <cassert>
#include <typeinfo>

// Check that the addresses of the typeinfo differ but still compare equal
// via their NTBS.
inline void
AssertIncompleteTypeInfoEquals(std::type_info const& LHS, std::type_info const& RHS)
{
  assert(&LHS != &RHS);
  assert(strcmp(LHS.name(), RHS.name()) == 0);
}

struct NeverDefined;
void ThrowNeverDefinedMP();
std::type_info const& ReturnTypeInfoNeverDefinedMP();

struct IncompleteAtThrow;
void ThrowIncompleteMP();
void ThrowIncompletePP();
void ThrowIncompletePMP();
std::type_info const& ReturnTypeInfoIncompleteMP();
std::type_info const& ReturnTypeInfoIncompletePP();

struct CompleteAtThrow;
void ThrowCompleteMP();
void ThrowCompletePP();
void ThrowCompletePMP();
std::type_info const& ReturnTypeInfoCompleteMP();
std::type_info const& ReturnTypeInfoCompletePP();

void ThrowNullptr();

#ifndef TU_ONE

void ThrowNeverDefinedMP() { throw (int NeverDefined::*)nullptr; }
std::type_info const& ReturnTypeInfoNeverDefinedMP() { return typeid(int NeverDefined::*); }

void ThrowIncompleteMP() { throw (int IncompleteAtThrow::*)nullptr; }
void ThrowIncompletePP() { throw (IncompleteAtThrow**)nullptr; }
void ThrowIncompletePMP() { throw (int IncompleteAtThrow::**)nullptr; }
std::type_info const& ReturnTypeInfoIncompleteMP() { return typeid(int IncompleteAtThrow::*); }
std::type_info const& ReturnTypeInfoIncompletePP() { return typeid(IncompleteAtThrow**); }

struct CompleteAtThrow {};
void ThrowCompleteMP() { throw (int CompleteAtThrow::*)nullptr; }
void ThrowCompletePP() { throw (CompleteAtThrow**)nullptr; }
void ThrowCompletePMP() { throw (int CompleteAtThrow::**)nullptr; }
std::type_info const& ReturnTypeInfoCompleteMP() { return typeid(int CompleteAtThrow::*); }
std::type_info const& ReturnTypeInfoCompletePP() { return typeid(CompleteAtThrow**); }

void ThrowNullptr() { throw nullptr; }

#else

struct IncompleteAtThrow {};

int main() {
  AssertIncompleteTypeInfoEquals(ReturnTypeInfoNeverDefinedMP(), typeid(int NeverDefined::*));
  try {
    ThrowNeverDefinedMP();
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (int NeverDefined::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch NeverDefined::*" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompleteMP(), typeid(int IncompleteAtThrow::*));
  try {
    ThrowIncompleteMP();
    assert(false);
  } catch (CompleteAtThrow**) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow::*" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompletePP(), typeid(IncompleteAtThrow**));
  try {
    ThrowIncompletePP();
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (IncompleteAtThrow** p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow**" ); }

  try {
    ThrowIncompletePMP();
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::**p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch IncompleteAtThrow::**" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompleteMP(), typeid(int CompleteAtThrow::*));
  try {
    ThrowCompleteMP();
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (CompleteAtThrow**) {
    assert(false);
  } catch (int CompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow::" ); }

  AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompletePP(), typeid(CompleteAtThrow**));
  try {
    ThrowCompletePP();
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (CompleteAtThrow**p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow**" ); }

  try {
    ThrowCompletePMP();
    assert(false);
  } catch (IncompleteAtThrow**) {
    assert(false);
  } catch (int IncompleteAtThrow::*) {
    assert(false);
  } catch (int CompleteAtThrow::*) {
    assert(false);
  } catch (CompleteAtThrow**) {
    assert(false);
  } catch (int CompleteAtThrow::**p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch CompleteAtThrow::**" ); }

#if __cplusplus >= 201103L
  // Catch nullptr as complete type
  try {
    ThrowNullptr();
  } catch (int IncompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch nullptr as IncompleteAtThrow::*" ); }

  // Catch nullptr as an incomplete type
  try {
    ThrowNullptr();
  } catch (int CompleteAtThrow::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch nullptr as CompleteAtThrow::*" ); }

  // Catch nullptr as a type that is never complete.
  try {
    ThrowNullptr();
  } catch (int NeverDefined::*p) {
    assert(!p);
  }
  catch(...) { assert(!"FAIL: Didn't catch nullptr as NeverDefined::*" ); }

#endif
}
#endif
<|endoftext|>


<commit_before>/*
  WString.cpp - String library for Wiring & Arduino
  Copyright (c) 2009-10 Hernando Barragan.  All rights reserved.

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

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

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdlib.h>
#include "WProgram.h"
#include "WString.h"

String::String( const char *value )
{
  if ( value == NULL )
    value = "";
  getBuffer( _length = strlen( value ) );
  if ( _buffer != NULL )
    strcpy( _buffer, value );
}

String::String( const String &value )
{
  getBuffer( _length = value._length );
  if ( _buffer != NULL )
    strcpy( _buffer, value._buffer );
}

String::String( const char value )
{
  _length = 1;
  getBuffer(1);
  if ( _buffer != NULL ) {
    _buffer[0] = value;
    _buffer[1] = 0;
  }
}

String::String( const unsigned char value )
{
  _length = 1;
  getBuffer(1);
  if ( _buffer != NULL) {
    _buffer[0] = value;
    _buffer[1] = 0;
  }
}

String::String( const int value, const int base )
{
  char buf[33];
  itoa((signed long)value, buf, base);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

String::String( const unsigned int value, const int base )
{
  char buf[33];
  ultoa((unsigned long)value, buf, base);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

String::String( const long value, const int base )
{
  char buf[33];
  ltoa(value, buf, base);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

String::String( const unsigned long value, const int base )
{
  char buf[33];
  ultoa(value, buf, 10);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

char String::charAt( unsigned int loc ) const
{
  return operator[]( loc );
}

void String::setCharAt( unsigned int loc, const char aChar )
{
  if(_buffer == NULL) return;
  if(_length > loc) {
    _buffer[loc] = aChar;
  }
}

int String::compareTo( const String &s2 ) const
{
  return strcmp( _buffer, s2._buffer );
}

const String & String::concat( const String &s2 )
{
  return (*this) += s2;
}

const String & String::operator=( const String &rhs )
{
  if ( this == &rhs )
    return *this;

  if ( rhs._length > _length )
  {
    free(_buffer);
    getBuffer( rhs._length );
  }

  if ( _buffer != NULL ) {
    _length = rhs._length;
    strcpy( _buffer, rhs._buffer );
  }
  return *this;
}

//const String & String::operator+=( const char aChar )
//{
//  if ( _length == _capacity )
//    doubleBuffer();
//
//  _buffer[ _length++ ] = aChar;
//  _buffer[ _length ] = '\0';
//  return *this;
//}

const String & String::operator+=( const String &other )
{
  _length += other._length;
  if ( _length > _capacity )
  {
    char *temp = _buffer;
    getBuffer( _length );
    if ( _buffer != NULL )
      strcpy( _buffer, temp );
    free(temp);
  }
  if ( _buffer != NULL )
    strcat( _buffer, other._buffer );
  return *this;
}

int String::operator==( const String &rhs ) const
{
  return ( _length == rhs._length && strcmp( _buffer, rhs._buffer ) == 0 );
}

int String::operator!=( const String &rhs ) const
{
  return ( _length != rhs.length() || strcmp( _buffer, rhs.toCharArray() ) != 0 );
}

int String::operator<( const String &rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) < 0;
}

int String::operator>( const String &rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) > 0;
}

int String::operator<=( const String &rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) <= 0;
}

int String::operator>=( const String & rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) >= 0;
}

char & String::operator[]( unsigned int index )
{
  // need to check for valid index, to do later
  return _buffer[ index ];
}

char String::operator[]( unsigned int index ) const
{
  // need to check for valid index, to do later
  return _buffer[ index ];
}

boolean String::endsWith( const String &s2 ) const
{
  if ( _length < s2._length )
    return 0;

  return strcmp( &_buffer[ _length - s2._length], s2.toCharArray() ) == 0;
}

boolean String::equals( const String &s2 ) const
{
  return ( _length == s2._length && strcmp( _buffer,s2._buffer ) == 0 );
}

boolean String::equalsIgnoreCase( const String &s2 ) const
{
  if ( this == &s2 )
    return true; //1;
  else if ( _length != s2._length )
    return false; //0;

  return strcmp(toLowerCase().toCharArray(), s2.toLowerCase().toCharArray()) == 0;
}

String String::replace( char findChar, char replaceChar )
{
  if ( _buffer == NULL ) return *this;
  String theReturn = _buffer;
  char* temp = theReturn._buffer;
  while( (temp = strchr( temp, findChar )) != 0 )
    *temp = replaceChar;

  return theReturn;
}

String String::replace( const String& match, const String& replace )
{
  if ( _buffer == NULL ) return *this;
  String temp = _buffer, newString;

  int loc;
  while ( (loc = temp.indexOf( match )) != -1 )
  {
    newString += temp.substring( 0, loc );
    newString += replace;
    temp = temp.substring( loc + match._length );
  }
  newString += temp;
  return newString;
}

int String::indexOf( char temp ) const
{
  return indexOf( temp, 0 );
}

int String::indexOf( char ch, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  const char* temp = strchr( &_buffer[fromIndex], ch );
  if ( temp == NULL )
    return -1;

  return temp - _buffer;
}

int String::indexOf( const String &s2 ) const
{
  return indexOf( s2, 0 );
}

int String::indexOf( const String &s2, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  const char *theFind = strstr( &_buffer[ fromIndex ], s2.toCharArray() );

  if ( theFind == NULL )
    return -1;

  return theFind - _buffer; // pointer subtraction
}

int String::lastIndexOf( char theChar ) const
{
  return lastIndexOf( theChar, _length - 1 );
}

int String::lastIndexOf( char ch, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  char tempchar = _buffer[fromIndex + 1];
  _buffer[fromIndex + 1] = '\0';
  char* temp = strrchr( _buffer, ch );
  _buffer[fromIndex + 1] = tempchar;

  if ( temp == NULL )
    return -1;

  return temp - _buffer;
}

int String::lastIndexOf( const String &s2 ) const
{
  return lastIndexOf( s2, _length - s2._length );
}

int String::lastIndexOf( const String &s2, unsigned int fromIndex ) const
{
  // check for empty strings
  if ( s2._length == 0 || s2._length - 1 > fromIndex || fromIndex >= _length )
    return -1;

  // matching first character
  char temp = s2[ 0 ];

  for ( int i = fromIndex; i >= 0; i-- )
  {
    if ( _buffer[ i ] == temp && (*this).substring( i, i + s2._length ).equals( s2 ) )
    return i;
  }
  return -1;
}

boolean String::startsWith( const String &s2 ) const
{
  if ( _length < s2._length )
    return 0;

  return startsWith( s2, 0 );
}

boolean String::startsWith( const String &s2, unsigned int offset ) const
{
  if ( offset > _length - s2._length )
    return 0;

  return strncmp( &_buffer[offset], s2.toCharArray(), s2._length ) == 0;
}

String String::substring( unsigned int left ) const
{
  return substring( left, _length );
}

String String::substring( unsigned int left, unsigned int right ) const
{
  if ( left > right )
  {
    int temp = right;
    right = left;
    left = temp;
  }

  if ( right > _length )
  {
    right = _length;
  }

  char temp = _buffer[ right ];  // save the replaced character
  _buffer[ right ] = '\0';
  String outPut = ( _buffer + left );  // pointer arithmetic
  _buffer[ right ] = temp;  //restore character
  return outPut;
}

String String::toLowerCase() const
{
  String temp = _buffer;

  for ( unsigned int i = 0; i < _length; i++ )
    temp._buffer[ i ] = (char)tolower( temp._buffer[ i ] );
  return temp;
}

String String::toUpperCase() const
{
  String temp = _buffer;

  for ( unsigned int i = 0; i < _length; i++ )
    temp._buffer[ i ] = (char)toupper( temp._buffer[ i ] );
  return temp;
}

String String::trim() const
{
  if ( _buffer == NULL ) return *this;
  String temp = _buffer;
  unsigned int i,j;

  for ( i = 0; i < _length; i++ )
  {
    if ( !isspace(_buffer[i]) )
      break;
  }

  for ( j = temp._length - 1; j > i; j-- )
  {
    if ( !isspace(_buffer[j]) )
      break;
  }

  return temp.substring( i, j + 1);
}

<commit_msg>Returning a reference to a dummy character for indices beyond the string length (in operator[]).<commit_after>/*
  WString.cpp - String library for Wiring & Arduino
  Copyright (c) 2009-10 Hernando Barragan.  All rights reserved.

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

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

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdlib.h>
#include "WProgram.h"
#include "WString.h"

String::String( const char *value )
{
  if ( value == NULL )
    value = "";
  getBuffer( _length = strlen( value ) );
  if ( _buffer != NULL )
    strcpy( _buffer, value );
}

String::String( const String &value )
{
  getBuffer( _length = value._length );
  if ( _buffer != NULL )
    strcpy( _buffer, value._buffer );
}

String::String( const char value )
{
  _length = 1;
  getBuffer(1);
  if ( _buffer != NULL ) {
    _buffer[0] = value;
    _buffer[1] = 0;
  }
}

String::String( const unsigned char value )
{
  _length = 1;
  getBuffer(1);
  if ( _buffer != NULL) {
    _buffer[0] = value;
    _buffer[1] = 0;
  }
}

String::String( const int value, const int base )
{
  char buf[33];
  itoa((signed long)value, buf, base);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

String::String( const unsigned int value, const int base )
{
  char buf[33];
  ultoa((unsigned long)value, buf, base);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

String::String( const long value, const int base )
{
  char buf[33];
  ltoa(value, buf, base);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

String::String( const unsigned long value, const int base )
{
  char buf[33];
  ultoa(value, buf, 10);
  getBuffer( _length = strlen(buf) );
  if ( _buffer != NULL )
    strcpy( _buffer, buf );
}

char String::charAt( unsigned int loc ) const
{
  return operator[]( loc );
}

void String::setCharAt( unsigned int loc, const char aChar )
{
  if(_buffer == NULL) return;
  if(_length > loc) {
    _buffer[loc] = aChar;
  }
}

int String::compareTo( const String &s2 ) const
{
  return strcmp( _buffer, s2._buffer );
}

const String & String::concat( const String &s2 )
{
  return (*this) += s2;
}

const String & String::operator=( const String &rhs )
{
  if ( this == &rhs )
    return *this;

  if ( rhs._length > _length )
  {
    free(_buffer);
    getBuffer( rhs._length );
  }

  if ( _buffer != NULL ) {
    _length = rhs._length;
    strcpy( _buffer, rhs._buffer );
  }
  return *this;
}

//const String & String::operator+=( const char aChar )
//{
//  if ( _length == _capacity )
//    doubleBuffer();
//
//  _buffer[ _length++ ] = aChar;
//  _buffer[ _length ] = '\0';
//  return *this;
//}

const String & String::operator+=( const String &other )
{
  _length += other._length;
  if ( _length > _capacity )
  {
    char *temp = _buffer;
    getBuffer( _length );
    if ( _buffer != NULL )
      strcpy( _buffer, temp );
    free(temp);
  }
  if ( _buffer != NULL )
    strcat( _buffer, other._buffer );
  return *this;
}

int String::operator==( const String &rhs ) const
{
  return ( _length == rhs._length && strcmp( _buffer, rhs._buffer ) == 0 );
}

int String::operator!=( const String &rhs ) const
{
  return ( _length != rhs.length() || strcmp( _buffer, rhs.toCharArray() ) != 0 );
}

int String::operator<( const String &rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) < 0;
}

int String::operator>( const String &rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) > 0;
}

int String::operator<=( const String &rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) <= 0;
}

int String::operator>=( const String & rhs ) const
{
  return strcmp( _buffer, rhs._buffer ) >= 0;
}

char & String::operator[]( unsigned int index )
{
  static char dummy_writable_char;
  if (index >= _length || !_buffer) {
    dummy_writable_char = 0;
    return dummy_writable_char;
  }
  return _buffer[ index ];
}

char String::operator[]( unsigned int index ) const
{
  // need to check for valid index, to do later
  return _buffer[ index ];
}

boolean String::endsWith( const String &s2 ) const
{
  if ( _length < s2._length )
    return 0;

  return strcmp( &_buffer[ _length - s2._length], s2.toCharArray() ) == 0;
}

boolean String::equals( const String &s2 ) const
{
  return ( _length == s2._length && strcmp( _buffer,s2._buffer ) == 0 );
}

boolean String::equalsIgnoreCase( const String &s2 ) const
{
  if ( this == &s2 )
    return true; //1;
  else if ( _length != s2._length )
    return false; //0;

  return strcmp(toLowerCase().toCharArray(), s2.toLowerCase().toCharArray()) == 0;
}

String String::replace( char findChar, char replaceChar )
{
  if ( _buffer == NULL ) return *this;
  String theReturn = _buffer;
  char* temp = theReturn._buffer;
  while( (temp = strchr( temp, findChar )) != 0 )
    *temp = replaceChar;

  return theReturn;
}

String String::replace( const String& match, const String& replace )
{
  if ( _buffer == NULL ) return *this;
  String temp = _buffer, newString;

  int loc;
  while ( (loc = temp.indexOf( match )) != -1 )
  {
    newString += temp.substring( 0, loc );
    newString += replace;
    temp = temp.substring( loc + match._length );
  }
  newString += temp;
  return newString;
}

int String::indexOf( char temp ) const
{
  return indexOf( temp, 0 );
}

int String::indexOf( char ch, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  const char* temp = strchr( &_buffer[fromIndex], ch );
  if ( temp == NULL )
    return -1;

  return temp - _buffer;
}

int String::indexOf( const String &s2 ) const
{
  return indexOf( s2, 0 );
}

int String::indexOf( const String &s2, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  const char *theFind = strstr( &_buffer[ fromIndex ], s2.toCharArray() );

  if ( theFind == NULL )
    return -1;

  return theFind - _buffer; // pointer subtraction
}

int String::lastIndexOf( char theChar ) const
{
  return lastIndexOf( theChar, _length - 1 );
}

int String::lastIndexOf( char ch, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  char tempchar = _buffer[fromIndex + 1];
  _buffer[fromIndex + 1] = '\0';
  char* temp = strrchr( _buffer, ch );
  _buffer[fromIndex + 1] = tempchar;

  if ( temp == NULL )
    return -1;

  return temp - _buffer;
}

int String::lastIndexOf( const String &s2 ) const
{
  return lastIndexOf( s2, _length - s2._length );
}

int String::lastIndexOf( const String &s2, unsigned int fromIndex ) const
{
  // check for empty strings
  if ( s2._length == 0 || s2._length - 1 > fromIndex || fromIndex >= _length )
    return -1;

  // matching first character
  char temp = s2[ 0 ];

  for ( int i = fromIndex; i >= 0; i-- )
  {
    if ( _buffer[ i ] == temp && (*this).substring( i, i + s2._length ).equals( s2 ) )
    return i;
  }
  return -1;
}

boolean String::startsWith( const String &s2 ) const
{
  if ( _length < s2._length )
    return 0;

  return startsWith( s2, 0 );
}

boolean String::startsWith( const String &s2, unsigned int offset ) const
{
  if ( offset > _length - s2._length )
    return 0;

  return strncmp( &_buffer[offset], s2.toCharArray(), s2._length ) == 0;
}

String String::substring( unsigned int left ) const
{
  return substring( left, _length );
}

String String::substring( unsigned int left, unsigned int right ) const
{
  if ( left > right )
  {
    int temp = right;
    right = left;
    left = temp;
  }

  if ( right > _length )
  {
    right = _length;
  }

  char temp = _buffer[ right ];  // save the replaced character
  _buffer[ right ] = '\0';
  String outPut = ( _buffer + left );  // pointer arithmetic
  _buffer[ right ] = temp;  //restore character
  return outPut;
}

String String::toLowerCase() const
{
  String temp = _buffer;

  for ( unsigned int i = 0; i < _length; i++ )
    temp._buffer[ i ] = (char)tolower( temp._buffer[ i ] );
  return temp;
}

String String::toUpperCase() const
{
  String temp = _buffer;

  for ( unsigned int i = 0; i < _length; i++ )
    temp._buffer[ i ] = (char)toupper( temp._buffer[ i ] );
  return temp;
}

String String::trim() const
{
  if ( _buffer == NULL ) return *this;
  String temp = _buffer;
  unsigned int i,j;

  for ( i = 0; i < _length; i++ )
  {
    if ( !isspace(_buffer[i]) )
      break;
  }

  for ( j = temp._length - 1; j > i; j-- )
  {
    if ( !isspace(_buffer[j]) )
      break;
  }

  return temp.substring( i, j + 1);
}

<|endoftext|>


<commit_before>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "generator.h"

#include <QtCore/QDebug>

#include <QDBusReply>
#include <QDBusMessage>
#include <QDBusConnection>

#include <kscreen/config.h>

bool Generator::forceLaptop = false;
bool Generator::forceLidClosed = false;
bool Generator::forceDocked = false;

KScreen::Config* Generator::idealConfig()
{
    KScreen::Config* config = KScreen::Config::current();
    KScreen::OutputList outputs = config->outputs();
    KScreen::OutputList connectedOutputs;

    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!output->isConnected()) {
            continue;
        }

        connectedOutputs.insert(output->id(), output);
    }

    //If we only have one screen, just select the preferred mode
    if (connectedOutputs.count() == 1) {
        qDebug() << "Config for one output";
        KScreen::Output* output = connectedOutputs.take(connectedOutputs.keys().first());
        output->setCurrentMode(output->preferredMode());

        return config;
    }

    //If we are a laptop, go into laptop mode
    if (Generator::isLaptop()) {
        return Generator::laptop();
    }

    qDebug() << "No ideal config found";
    return new KScreen::Config();
}

bool Generator::isLaptop()
{
    if (Generator::forceLaptop) {
        return true;
    }

    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.UPower",
                                   "/org/freedesktop/UPower",
                                   "org.freedesktop.DBus.Properties",
                                   "Get");
    QVariantList args;
    args << "org.freedesktop.UPower";
    args << "LidIsPresent";
    msg.setArguments(args);

    QDBusReply<bool> reply = QDBusConnection::systemBus().call(msg);
    return reply.value();
}

KScreen::Config* Generator::laptop()
{
    qDebug() << "Config for a laptop";
    KScreen::Config* config = KScreen::Config::current();
    KScreen::OutputList outputs = config->outputs();

    KScreen::Output* embedded;
    KScreen::Output* external;
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!output->isConnected()) {
            continue;
        }
        if (Generator::isEmbedded(output->name())) {
            embedded = output;
            continue;
        }
        external = output;
    }

    if (Generator::isLidClosed()) {
        qDebug() << "With lid closed";
        embedded->setEnabled(false);
        external->setEnabled(true);
        external->setCurrentMode(external->preferredMode());
        external->setPrimary(true);

        return config;
    }

    embedded->setPos(QPoint(0,0));
    embedded->setCurrentMode(embedded->preferredMode());
    embedded->setPrimary(true);
    embedded->setEnabled(true);

    QSize size = embedded->mode(embedded->preferredMode())->size();
    external->setPos(QPoint(size.width(), 0));
    external->setEnabled(true);
    external->setCurrentMode(external->preferredMode());
    external->setPrimary(false);

    if (Generator::isDocked()) {
        qDebug() << "Docked";
        embedded->setPrimary(false);
        external->setPrimary(true);
    }

    return config;
}

bool Generator::isEmbedded(const QString& name)
{
    QStringList embedded;
    embedded << "LVDS";
    embedded << "IDP";
    embedded << "EDP";

    Q_FOREACH(const QString &pre, embedded) {
        if (name.toUpper().startsWith(pre)) {
            qDebug() << "This is embedded: " << name;
            return true;
        }
    }

    return false;
}

bool Generator::isLidClosed()
{
    if (Generator::forceLidClosed) {
        return true;
    }

    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.UPower",
                                                      "/org/freedesktop/UPower",
                                                      "org.freedesktop.DBus.Properties",
                                                      "Get");
    QVariantList args;
    args << "org.freedesktop.UPower";
    args << "LidIsClosed";
    msg.setArguments(args);

    QDBusReply<bool> reply = QDBusConnection::systemBus().call(msg);
    return reply.value();
}

bool Generator::isDocked()
{
    if (Generator::forceDocked) {
        return true;
    }

    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.UPower",
                                                      "/org/freedesktop/UPower",
                                                      "org.freedesktop.DBus.Properties",
                                                      "Get");
    QVariantList args;
    args << "org.freedesktop.UPower";
    args << "IsDocked";
    msg.setArguments(args);

    QDBusReply<bool> reply = QDBusConnection::systemBus().call(msg);
    return reply.value();
}

KScreen::Config* Generator::dockedLaptop()
{
    return new KScreen::Config();
}

KScreen::Config* Generator::desktop()
{
    return new KScreen::Config();
}<commit_msg>Apparently we are getting a variant from UPower instead of bool<commit_after>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "generator.h"

#include <QtCore/QDebug>

#include <QDBusReply>
#include <QDBusMessage>
#include <QDBusConnection>

#include <kscreen/config.h>

bool Generator::forceLaptop = false;
bool Generator::forceLidClosed = false;
bool Generator::forceDocked = false;

KScreen::Config* Generator::idealConfig()
{
    KScreen::Config* config = KScreen::Config::current();
    KScreen::OutputList outputs = config->outputs();
    KScreen::OutputList connectedOutputs;

    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!output->isConnected()) {
            continue;
        }

        connectedOutputs.insert(output->id(), output);
    }

    //If we only have one screen, just select the preferred mode
    if (connectedOutputs.count() == 1) {
        qDebug() << "Config for one output";
        KScreen::Output* output = connectedOutputs.take(connectedOutputs.keys().first());
        output->setCurrentMode(output->preferredMode());

        return config;
    }

    //If we are a laptop, go into laptop mode
    if (Generator::isLaptop()) {
        return Generator::laptop();
    }

    qDebug() << "No ideal config found";
    return new KScreen::Config();
}

bool Generator::isLaptop()
{
    if (Generator::forceLaptop) {
        return true;
    }

    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.UPower",
                                   "/org/freedesktop/UPower",
                                   "org.freedesktop.DBus.Properties",
                                   "Get");
    QVariantList args;
    args << "org.freedesktop.UPower";
    args << "LidIsPresent";
    msg.setArguments(args);

    QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
    return reply.value().toBool();
}

KScreen::Config* Generator::laptop()
{
    qDebug() << "Config for a laptop";
    KScreen::Config* config = KScreen::Config::current();
    KScreen::OutputList outputs = config->outputs();

    KScreen::Output* embedded;
    KScreen::Output* external;
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!output->isConnected()) {
            continue;
        }
        if (Generator::isEmbedded(output->name())) {
            embedded = output;
            continue;
        }
        external = output;
    }

    if (Generator::isLidClosed()) {
        qDebug() << "With lid closed";
        embedded->setEnabled(false);
        external->setEnabled(true);
        external->setCurrentMode(external->preferredMode());
        external->setPrimary(true);

        return config;
    }

    embedded->setPos(QPoint(0,0));
    embedded->setCurrentMode(embedded->preferredMode());
    embedded->setPrimary(true);
    embedded->setEnabled(true);

    QSize size = embedded->mode(embedded->preferredMode())->size();
    external->setPos(QPoint(size.width(), 0));
    external->setEnabled(true);
    external->setCurrentMode(external->preferredMode());
    external->setPrimary(false);

    if (Generator::isDocked()) {
        qDebug() << "Docked";
        embedded->setPrimary(false);
        external->setPrimary(true);
    }

    return config;
}

bool Generator::isEmbedded(const QString& name)
{
    QStringList embedded;
    embedded << "LVDS";
    embedded << "IDP";
    embedded << "EDP";

    Q_FOREACH(const QString &pre, embedded) {
        if (name.toUpper().startsWith(pre)) {
            qDebug() << "This is embedded: " << name;
            return true;
        }
    }

    return false;
}

bool Generator::isLidClosed()
{
    if (Generator::forceLidClosed) {
        return true;
    }

    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.UPower",
                                                      "/org/freedesktop/UPower",
                                                      "org.freedesktop.DBus.Properties",
                                                      "Get");
    QVariantList args;
    args << "org.freedesktop.UPower";
    args << "LidIsClosed";
    msg.setArguments(args);

    QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
    return reply.value().toBool();
}

bool Generator::isDocked()
{
    if (Generator::forceDocked) {
        return true;
    }

    QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.UPower",
                                                      "/org/freedesktop/UPower",
                                                      "org.freedesktop.DBus.Properties",
                                                      "Get");
    QVariantList args;
    args << "org.freedesktop.UPower";
    args << "IsDocked";
    msg.setArguments(args);

    QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
    return reply.value().toBool();
}

KScreen::Config* Generator::dockedLaptop()
{
    return new KScreen::Config();
}

KScreen::Config* Generator::desktop()
{
    return new KScreen::Config();
}<|endoftext|>


<commit_before>#include "compiler.h"

#include <QString>
#include <QList>
#include <QSet>
#include <QVector>

using namespace std;

/* Implementation note: wire naming convention
 *
 * All wire names must be unique. To ensure uniqueness, their names are a
 * combination of the block number and pin name of the pin that drives it,
 * as follows:
 *
 * wire_<blocknumber>_<pinname>
 */

QString generatePicCode(FlowChart flow) {
    QSet<QString> blockNames = extractUniqueBlockNames(flow);
    QString mainFile = "int main() {\n\n";
    QSet<int> expanded;
    QList<int> toBeExpanded = extractInputBlocks(flow);
    QSet<QString> wires = extractWireNames(flow);
    // directed graph traversal: write the sequence of functions
    while(!toBeExpanded.empty()) {
        int blockIndex = toBeExpanded.front();
        toBeExpanded.pop_front();
        // if this block has not been expanded yet
        if (expanded.find(blockIndex) == expanded.end()) {
            Block block = flow.block(blockIndex);
            // check if all this block's inputs have been expanded
            bool found_all = isExpandable(block, expanded);
            if(found_all) {
                // mark this node as expanded
                expanded.insert(blockIndex);
                // add all of this node's outputs to the front of the queue
                for(auto output : block.outputConnections().values()) {
                    for(BlockPin element : output){
                      int outIndex = element.blockNum();
                      if (!expanded.contains(outIndex)) {
                        toBeExpanded.push_front(outIndex);
                      }
                    }
                }
                // write this node's function call
                QVector<BlockPin> wireBlockPins;
                for (QString inputPinName : block.blockType().inputs().keys()) {
                    if (!block.inputIsConnected(inputPinName)) {
                        // error: bad structure
                        return "";
                    } else {
                        BlockPin wireSource = block.inputConnections()[inputPinName];
                        wireBlockPins.push_back(wireSource);
                    }
                }
                for (QString outputPinName : block.blockType().outputs().keys()) {
                    BlockPin outputBlockPin(blockIndex, outputPinName);
                    wireBlockPins.push_back(outputBlockPin);
                }
                QString funcCall = block.blockType().name();
                funcCall += "(";
                for (int i = 0; i < wireBlockPins.size(); i++) {
                    BlockPin wireSource = wireBlockPins[i];
                    funcCall += "&wire_";
                    funcCall += QString::number(wireSource.blockNum());
                    funcCall += "_";
                    funcCall += wireSource.pinName();
                    if (i < wireBlockPins.size() - 1) {
                        funcCall += ",";
                    }
                }
                funcCall += ");";
                mainFile += funcCall;
                mainFile += "\n";
            } else {
               toBeExpanded.push_back(blockIndex);
            }
        }
    }
    mainFile += "\nreturn 0;\n\n}";

    QString includesFile = "";
    for (QString blockName : blockNames) {
        includesFile += "#include \"blocks/";
        includesFile += blockName;
        includesFile += ".h\"\n";
    }
    includesFile += "\n";

    QString declarationsFile = "";
    for (QString wireName : wires) {
        declarationsFile += "int ";
        declarationsFile += wireName;
        declarationsFile += ";\n";
    }
    declarationsFile += "\n";

    return includesFile + declarationsFile + mainFile;
}

QSet<QString> extractUniqueBlockNames(FlowChart flow) {
    QSet<QString> blockNames;
    for(Block block : flow.blocks().values()) {
        // make sure this block's name is in the blockNames set
        blockNames.insert(block.blockType().name());
    }
    return blockNames;
}

QList<int> extractInputBlocks(FlowChart flow) {
    QList<int> inputBlocks;
    for(int blockIndex : flow.blocks().keys()) {
        Block block = flow.block(blockIndex);
        // If this is an input block, make sure it's expanded first
        if(block.inputConnections().empty()) {
            inputBlocks.push_back(blockIndex);
        }
    }
    return inputBlocks;
}

QSet<QString> extractWireNames(FlowChart flow) {
    QSet<QString> wireNames;
    for(int blockIndex : flow.blocks().keys()) {
        Block block = flow.block(blockIndex);
        // for each output connection
        for (QString pinName : block.outputConnections().keys()) {
            // construct wire name
            QString wireName = "wire_";
            wireName += QString::number(blockIndex);
            wireName += "_";
            wireName += pinName;
            wireNames.insert(wireName);
        }
    }
    return wireNames;
}

bool isExpandable(Block block, QSet<int> expanded) {
    bool found_all = true;
    for(BlockPin bp : block.inputConnections().values()) {
        int blockNum = bp.blockNum();
        if(expanded.find(blockNum) == expanded.end()) {
            found_all = false;
            break;
        }
    }
    return found_all;
}
<commit_msg>Add avr/io.h to compiler<commit_after>#include "compiler.h"

#include <QString>
#include <QList>
#include <QSet>
#include <QVector>

using namespace std;

/* Implementation note: wire naming convention
 *
 * All wire names must be unique. To ensure uniqueness, their names are a
 * combination of the block number and pin name of the pin that drives it,
 * as follows:
 *
 * wire_<blocknumber>_<pinname>
 */

QString generatePicCode(FlowChart flow) {
    QSet<QString> blockNames = extractUniqueBlockNames(flow);
    QString mainFile = "int main() {\n\n";
    QSet<int> expanded;
    QList<int> toBeExpanded = extractInputBlocks(flow);
    QSet<QString> wires = extractWireNames(flow);
    // directed graph traversal: write the sequence of functions
    while(!toBeExpanded.empty()) {
        int blockIndex = toBeExpanded.front();
        toBeExpanded.pop_front();
        // if this block has not been expanded yet
        if (expanded.find(blockIndex) == expanded.end()) {
            Block block = flow.block(blockIndex);
            // check if all this block's inputs have been expanded
            bool found_all = isExpandable(block, expanded);
            if(found_all) {
                // mark this node as expanded
                expanded.insert(blockIndex);
                // add all of this node's outputs to the front of the queue
                for(auto output : block.outputConnections().values()) {
                    for(BlockPin element : output){
                      int outIndex = element.blockNum();
                      if (!expanded.contains(outIndex)) {
                        toBeExpanded.push_front(outIndex);
                      }
                    }
                }
                // write this node's function call
                QVector<BlockPin> wireBlockPins;
                for (QString inputPinName : block.blockType().inputs().keys()) {
                    if (!block.inputIsConnected(inputPinName)) {
                        // error: bad structure
                        return "";
                    } else {
                        BlockPin wireSource = block.inputConnections()[inputPinName];
                        wireBlockPins.push_back(wireSource);
                    }
                }
                for (QString outputPinName : block.blockType().outputs().keys()) {
                    BlockPin outputBlockPin(blockIndex, outputPinName);
                    wireBlockPins.push_back(outputBlockPin);
                }
                QString funcCall = block.blockType().name();
                funcCall += "(";
                for (int i = 0; i < wireBlockPins.size(); i++) {
                    BlockPin wireSource = wireBlockPins[i];
                    funcCall += "&wire_";
                    funcCall += QString::number(wireSource.blockNum());
                    funcCall += "_";
                    funcCall += wireSource.pinName();
                    if (i < wireBlockPins.size() - 1) {
                        funcCall += ",";
                    }
                }
                funcCall += ");";
                mainFile += funcCall;
                mainFile += "\n";
            } else {
               toBeExpanded.push_back(blockIndex);
            }
        }
    }
    mainFile += "\nreturn 0;\n\n}";

    QString includesFile = "#include <avr/io.h>\n";
    for (QString blockName : blockNames) {
        includesFile += "#include \"blocks/";
        includesFile += blockName;
        includesFile += ".h\"\n";
    }
    includesFile += "\n";

    QString declarationsFile = "";
    for (QString wireName : wires) {
        declarationsFile += "int ";
        declarationsFile += wireName;
        declarationsFile += ";\n";
    }
    declarationsFile += "\n";

    return includesFile + declarationsFile + mainFile;
}

QSet<QString> extractUniqueBlockNames(FlowChart flow) {
    QSet<QString> blockNames;
    for(Block block : flow.blocks().values()) {
        // make sure this block's name is in the blockNames set
        blockNames.insert(block.blockType().name());
    }
    return blockNames;
}

QList<int> extractInputBlocks(FlowChart flow) {
    QList<int> inputBlocks;
    for(int blockIndex : flow.blocks().keys()) {
        Block block = flow.block(blockIndex);
        // If this is an input block, make sure it's expanded first
        if(block.inputConnections().empty()) {
            inputBlocks.push_back(blockIndex);
        }
    }
    return inputBlocks;
}

QSet<QString> extractWireNames(FlowChart flow) {
    QSet<QString> wireNames;
    for(int blockIndex : flow.blocks().keys()) {
        Block block = flow.block(blockIndex);
        // for each output connection
        for (QString pinName : block.outputConnections().keys()) {
            // construct wire name
            QString wireName = "wire_";
            wireName += QString::number(blockIndex);
            wireName += "_";
            wireName += pinName;
            wireNames.insert(wireName);
        }
    }
    return wireNames;
}

bool isExpandable(Block block, QSet<int> expanded) {
    bool found_all = true;
    for(BlockPin bp : block.inputConnections().values()) {
        int blockNum = bp.blockNum();
        if(expanded.find(blockNum) == expanded.end()) {
            found_all = false;
            break;
        }
    }
    return found_all;
}
<|endoftext|>


<commit_before>#include "generator.h"
#include "opcodes.h"
#include "sections.h"

#include <iostream>
#include <iomanip>

namespace ceos {
  std::stringstream &Generator::generate() {
    generateProgram(m_ast);
    m_output.seekg(0);
    return m_output;
  }

  void Generator::generateNode(std::shared_ptr<AST> node) {
    switch (node->type) {
      case AST::Type::Call:
        generateCall(std::static_pointer_cast<AST::Call>(node));
        break;
      case AST::Type::Number:
        generateNumber(std::static_pointer_cast<AST::Number>(node));
        break;
      case AST::Type::ID:
        generateID(std::static_pointer_cast<AST::ID>(node));
        break;
      case AST::Type::String:
        generateString(std::static_pointer_cast<AST::String>(node));
        break;
      case AST::Type::FunctionArgument:
        generateFunctionArgument(std::static_pointer_cast<AST::FunctionArgument>(node));
        break;
      default:
        throw "Unhandled Type";
    }
  }

  bool Generator::handleSpecialCall(std::shared_ptr<AST::Call> call) {
    if (call->arguments[0]->type == AST::Type::ID) {
      std::string callee = AST::asID(call->arguments[0])->name;

      if (callee == "defn" || callee == "lambda") {
        if (callee == "lambda") {
          static unsigned lambdaID = 0;
          std::stringstream lambdaName;
          lambdaName << "lambda$" << lambdaID++;
          m_ast->strings.push_back(lambdaName.str());
        } else {
          auto name = AST::asID(call->arguments[1])->name;
        }

        emitOpcode(Opcode::create_closure);
        write(m_ast->functions.size());

        if (callee == "defn") {
          emitOpcode(Opcode::bind);
        }

        m_ast->functions.push_back(call);
        return true;
      } else if (callee == "if") {
        generateIf(call);
        return true;
      }
    }

    return false;
  }

  void Generator::write(int data) {
    m_output.write(reinterpret_cast<char *>(&data), sizeof(data));
  }

  void Generator::write(const std::string &data) {
    m_output << data;
    m_output.put('\0');
  }

  void Generator::emitOpcode(Opcode::Type opcode) {
    write(opcode);
  }

  void Generator::emitJmp(Opcode::Type jmpType, std::shared_ptr<AST> &node)  {
    emitJmp(jmpType, node, false);
  }

  void Generator::emitJmp(Opcode::Type jmpType, std::shared_ptr<AST> &node, bool skipNextJump)  {
    emitOpcode(jmpType);
    unsigned beforePos = m_output.tellp();
    write(0); // placeholder

    generateNode(node);
    unsigned afterPos = m_output.tellp();
    m_output.seekp(beforePos);
    write((afterPos - beforePos)
        - 4 // sizeof the jump offset
        + (skipNextJump ? 8 : 0) // special case for if with else
    );
    m_output.seekp(afterPos);
  }

  void Generator::generateCall(std::shared_ptr<AST::Call> call) {
    if (handleSpecialCall(call)) {
      return;
    }

    for (unsigned i = call->arguments.size(); i > 0;) {
      generateNode(call->arguments[--i]);
    }

    emitOpcode(Opcode::call);
    write(call->arguments.size() - 1); // don't include the callee
  }

  void Generator::generateNumber(std::shared_ptr<AST::Number> number) {
    emitOpcode(Opcode::push);
    write(number->value);
  }

  void Generator::generateID(std::shared_ptr<AST::ID> id) {
    auto v = m_scope->get(id->name);
    if (v.get()) {
      generateNode(v);
    } else {
      emitOpcode(Opcode::lookup);
      write(id->uid);
    }
  }

  void Generator::generateString(std::shared_ptr<AST::String> str) {
    emitOpcode(Opcode::load_string);
    write(str->uid);
  }

  void Generator::generateFunctionArgument(std::shared_ptr<AST::FunctionArgument> arg) {
    emitOpcode(Opcode::push_arg);
    write(arg->index);
  }

  void Generator::generateFunction(std::shared_ptr<AST::Call> fn) {
    auto callee = AST::asID(fn->arguments[0])->name;
    int index;
    std::string name;
    if (callee == "defn") {
      index = 1;
      name = AST::asID(fn->arguments[1])->name;
    } else if (callee == "lambda") {
      index = 0;
      static unsigned lambdaID = 0;
      std::stringstream lambdaName;
      lambdaName << "lambda$" << lambdaID++;
      name = lambdaName.str();
    } else {
      throw;
    }

    write(INDEX_OF(m_ast->strings, name));
    write(AST::asCall(fn->arguments[index + 1])->arguments.size());

    m_scope = m_scope->create();

    int i = 0;
    for (auto arg : AST::asCall(fn->arguments[index + 1])->arguments) {
      std::string &name = AST::asID(arg)->name;
      m_scope->set(name, std::make_shared<AST::FunctionArgument>(i++));
      write(INDEX_OF(m_ast->strings, name));
    }

    for (unsigned ii = index + 2; ii < fn->arguments.size(); ii++) {
      generateNode(fn->arguments[ii]);
    }

    m_scope = m_scope->restore();

    write(Opcode::ret);
  }

  void Generator::generateIf(std::shared_ptr<AST::Call> iff) {
    unsigned size = iff->arguments.size();
    assert(size == 3 || size == 4);

    generateNode(iff->arguments[1]);

    emitJmp(Opcode::jz, iff->arguments[2], size == 4 ? 2 : 0);

    if (size == 4) {
      emitJmp(Opcode::jmp, iff->arguments[3]);
    }
  }

  void Generator::generateProgram(std::shared_ptr<AST::Program> program) {
    for (auto node : program->nodes()) {
      generateNode(node);
    }

    auto text = m_output.str();
    m_output = std::stringstream();

    if (program->functions.size()) {
      unsigned i = 0;
      while (i < program->functions.size()) {
        write(Section::FunctionHeader);
        generateFunction(program->functions[i++]);
      }
    }

    auto functions = m_output.str();
    m_output = std::stringstream();

    if (program->strings.size()) {
      write(Section::Header);
      write(Section::Strings);

      for (auto string : program->strings) {
        write(string);
      }
    }

    if (functions.length()) {
      write(Section::Header);
      write(Section::Functions);
      m_output << functions;
    }

    write(Section::Header);
    write(Section::Text);
    m_output << text;
  }

  static std::vector<std::string> strings;
  static size_t width;
  static std::string padding("  ");
  void Generator::disassemble(std::stringstream &bytecode) {
    size_t size = bytecode.str().length();
    width = std::ceil(std::log10(size + 1)) + 1;

#define WRITE(OFFSET, ...) \
    std::cout << "[" \
    <<  std::setfill(' ') << std::setw(width) << ((int)bytecode.tellg() - (OFFSET)) \
    << "] " << padding << __VA_ARGS__ << "\n"

read_section:
    READ_INT(bytecode, ceos);
    assert(ceos == 0xCE05);

    READ_INT(bytecode, section);

    switch (section) {
      case Section::Strings:
        padding = "";
        WRITE(8, "STRINGS:");
        padding = "  ";
        goto section_strings;
        break;
      case Section::Functions:
        padding = "";
        WRITE(8, "FUNCTIONS:");
        padding = "  ";
        goto section_functions;
        break;
      case Section::Text:
        padding = "";
        WRITE(8, "TEXT:");
        padding = "  ";
        goto section_code;
        break;
    }

section_strings:
    while (true) {
      READ_INT(bytecode, ceos);
      bytecode.seekg(-4, bytecode.cur);
      if (ceos == 0xCE05)  {
        goto read_section;
      }
      static unsigned str_index = 0;
      READ_STR(bytecode, str);
      strings.push_back(str);
      WRITE(str.length() + 1, "$" << str_index++ << ": " << str);
    }

section_functions: {
      READ_INT(bytecode, fn_header);
      assert(fn_header == Section::FunctionHeader);

      READ_INT(bytecode, fn_id);
      READ_INT(bytecode, arg_count);

      std::stringstream args;
      for (int i = 0; i < arg_count; i++) {
        READ_INT(bytecode, argID);
        args << "$" << i << ": " << strings[argID];
        if (i < arg_count - 1) args << ", ";
      }

      padding = "";
      WRITE(8, strings[fn_id] << "(" << args.str() << "):");
      padding = "  ";

      while (true) {
        READ_INT(bytecode, header);
        bytecode.seekg(-4, bytecode.cur);
        if (header == Section::FunctionHeader) {
          goto section_functions;
        } else if (header == Section::Header) {
          goto read_section;
        }

        READ_INT(bytecode, opcode);
        printOpcode(bytecode, static_cast<Opcode::Type>(opcode));
      }
    }

section_code:
    while (true) {
      READ_INT(bytecode, opcode);
      printOpcode(bytecode, static_cast<Opcode::Type>(opcode));
    }
    goto read_section;
  }

  void Generator::printOpcode(std::stringstream &bytecode, Opcode::Type opcode) {
    size_t size = bytecode.str().length();
    size_t width = std::ceil(std::log10(size + 1)) + 1;

    switch (opcode) {
      case Opcode::push: {
        READ_INT(bytecode, value);
        WRITE(8, "push 0x" << std::setbase(16) << value << std::setbase(10));
        break;
      }
      case Opcode::call: {
        READ_INT(bytecode, nargs);
        WRITE(8, "call (" << nargs << ")");
        break;
      }
      case Opcode::load_string: {
        READ_INT(bytecode, stringID);
        WRITE(8, "load_string $" << stringID);
        break;
      }
      case Opcode::lookup: {
        READ_INT(bytecode, id);
        WRITE(8, "lookup $" << id << "(" << strings[id] << ")");
        break;
      }
      case Opcode::create_closure: {
        READ_INT(bytecode, id);
        WRITE(8, "create_closure " << strings[id]);
        break;
      }
      case Opcode::jmp:  {
        READ_INT(bytecode, target);
        WRITE(8, "jmp [" << ((int)bytecode.tellg() + target) << "]");
        break;
      }
      case Opcode::jz: {
        READ_INT(bytecode, target);
        WRITE(8, "jz [" << ((int)bytecode.tellg() + target) << "]");
        break;
      }
      case Opcode::push_arg: {
        READ_INT(bytecode, arg);
        WRITE(8, "push_arg $" << arg);
        break;
      }
      case Opcode::ret: {
        WRITE(4, "ret");
        break;
      }
      case Opcode::bind:
        WRITE(4, "bind");
        break;
      default:
        std::cerr << "Unhandled opcode: " << Opcode::typeName(static_cast<Opcode::Type>(opcode)) << "\n";
        throw;
    }
  }
#undef WRITE
}
<commit_msg>Fix dumping the function name in create_closure<commit_after>#include "generator.h"
#include "opcodes.h"
#include "sections.h"

#include <iostream>
#include <iomanip>

namespace ceos {
  std::stringstream &Generator::generate() {
    generateProgram(m_ast);
    m_output.seekg(0);
    return m_output;
  }

  void Generator::generateNode(std::shared_ptr<AST> node) {
    switch (node->type) {
      case AST::Type::Call:
        generateCall(std::static_pointer_cast<AST::Call>(node));
        break;
      case AST::Type::Number:
        generateNumber(std::static_pointer_cast<AST::Number>(node));
        break;
      case AST::Type::ID:
        generateID(std::static_pointer_cast<AST::ID>(node));
        break;
      case AST::Type::String:
        generateString(std::static_pointer_cast<AST::String>(node));
        break;
      case AST::Type::FunctionArgument:
        generateFunctionArgument(std::static_pointer_cast<AST::FunctionArgument>(node));
        break;
      default:
        throw "Unhandled Type";
    }
  }

  bool Generator::handleSpecialCall(std::shared_ptr<AST::Call> call) {
    if (call->arguments[0]->type == AST::Type::ID) {
      std::string callee = AST::asID(call->arguments[0])->name;

      if (callee == "defn" || callee == "lambda") {
        if (callee == "lambda") {
          static unsigned lambdaID = 0;
          std::stringstream lambdaName;
          lambdaName << "lambda$" << lambdaID++;
          m_ast->strings.push_back(lambdaName.str());
        } else {
          auto name = AST::asID(call->arguments[1])->name;
        }

        emitOpcode(Opcode::create_closure);
        write(m_ast->functions.size());

        if (callee == "defn") {
          emitOpcode(Opcode::bind);
        }

        m_ast->functions.push_back(call);
        return true;
      } else if (callee == "if") {
        generateIf(call);
        return true;
      }
    }

    return false;
  }

  void Generator::write(int data) {
    m_output.write(reinterpret_cast<char *>(&data), sizeof(data));
  }

  void Generator::write(const std::string &data) {
    m_output << data;
    m_output.put('\0');
  }

  void Generator::emitOpcode(Opcode::Type opcode) {
    write(opcode);
  }

  void Generator::emitJmp(Opcode::Type jmpType, std::shared_ptr<AST> &node)  {
    emitJmp(jmpType, node, false);
  }

  void Generator::emitJmp(Opcode::Type jmpType, std::shared_ptr<AST> &node, bool skipNextJump)  {
    emitOpcode(jmpType);
    unsigned beforePos = m_output.tellp();
    write(0); // placeholder

    generateNode(node);
    unsigned afterPos = m_output.tellp();
    m_output.seekp(beforePos);
    write((afterPos - beforePos)
        - 4 // sizeof the jump offset
        + (skipNextJump ? 8 : 0) // special case for if with else
    );
    m_output.seekp(afterPos);
  }

  void Generator::generateCall(std::shared_ptr<AST::Call> call) {
    if (handleSpecialCall(call)) {
      return;
    }

    for (unsigned i = call->arguments.size(); i > 0;) {
      generateNode(call->arguments[--i]);
    }

    emitOpcode(Opcode::call);
    write(call->arguments.size() - 1); // don't include the callee
  }

  void Generator::generateNumber(std::shared_ptr<AST::Number> number) {
    emitOpcode(Opcode::push);
    write(number->value);
  }

  void Generator::generateID(std::shared_ptr<AST::ID> id) {
    auto v = m_scope->get(id->name);
    if (v.get()) {
      generateNode(v);
    } else {
      emitOpcode(Opcode::lookup);
      write(id->uid);
    }
  }

  void Generator::generateString(std::shared_ptr<AST::String> str) {
    emitOpcode(Opcode::load_string);
    write(str->uid);
  }

  void Generator::generateFunctionArgument(std::shared_ptr<AST::FunctionArgument> arg) {
    emitOpcode(Opcode::push_arg);
    write(arg->index);
  }

  void Generator::generateFunction(std::shared_ptr<AST::Call> fn) {
    auto callee = AST::asID(fn->arguments[0])->name;
    int index;
    std::string name;
    if (callee == "defn") {
      index = 1;
      name = AST::asID(fn->arguments[1])->name;
    } else if (callee == "lambda") {
      index = 0;
      static unsigned lambdaID = 0;
      std::stringstream lambdaName;
      lambdaName << "lambda$" << lambdaID++;
      name = lambdaName.str();
    } else {
      throw;
    }

    write(INDEX_OF(m_ast->strings, name));
    write(AST::asCall(fn->arguments[index + 1])->arguments.size());

    m_scope = m_scope->create();

    int i = 0;
    for (auto arg : AST::asCall(fn->arguments[index + 1])->arguments) {
      std::string &name = AST::asID(arg)->name;
      m_scope->set(name, std::make_shared<AST::FunctionArgument>(i++));
      write(INDEX_OF(m_ast->strings, name));
    }

    for (unsigned ii = index + 2; ii < fn->arguments.size(); ii++) {
      generateNode(fn->arguments[ii]);
    }

    m_scope = m_scope->restore();

    write(Opcode::ret);
  }

  void Generator::generateIf(std::shared_ptr<AST::Call> iff) {
    unsigned size = iff->arguments.size();
    assert(size == 3 || size == 4);

    generateNode(iff->arguments[1]);

    emitJmp(Opcode::jz, iff->arguments[2], size == 4 ? 2 : 0);

    if (size == 4) {
      emitJmp(Opcode::jmp, iff->arguments[3]);
    }
  }

  void Generator::generateProgram(std::shared_ptr<AST::Program> program) {
    for (auto node : program->nodes()) {
      generateNode(node);
    }

    auto text = m_output.str();
    m_output = std::stringstream();

    if (program->functions.size()) {
      unsigned i = 0;
      while (i < program->functions.size()) {
        write(Section::FunctionHeader);
        generateFunction(program->functions[i++]);
      }
    }

    auto functions = m_output.str();
    m_output = std::stringstream();

    if (program->strings.size()) {
      write(Section::Header);
      write(Section::Strings);

      for (auto string : program->strings) {
        write(string);
      }
    }

    if (functions.length()) {
      write(Section::Header);
      write(Section::Functions);
      m_output << functions;
    }

    write(Section::Header);
    write(Section::Text);
    m_output << text;
  }

  static std::vector<std::string> strings;
  static std::vector<std::string> functions;
  static size_t width;
  static std::string padding("  ");
  void Generator::disassemble(std::stringstream &bytecode) {
    size_t size = bytecode.str().length();
    width = std::ceil(std::log10(size + 1)) + 1;

#define WRITE(OFFSET, ...) \
    std::cout << "[" \
    <<  std::setfill(' ') << std::setw(width) << ((int)bytecode.tellg() - (OFFSET)) \
    << "] " << padding << __VA_ARGS__ << "\n"

read_section:
    READ_INT(bytecode, ceos);
    assert(ceos == 0xCE05);

    READ_INT(bytecode, section);

    switch (section) {
      case Section::Strings:
        padding = "";
        WRITE(8, "STRINGS:");
        padding = "  ";
        goto section_strings;
        break;
      case Section::Functions: {
        padding = "";
        WRITE(8, "FUNCTIONS:");
        padding = "  ";
        auto offset = bytecode.tellg();
        while (true) {
          READ_INT(bytecode, header);
          if (header == Section::FunctionHeader) {
            READ_INT(bytecode, fnID);
            functions.push_back(strings[fnID]);
            continue;
          } else if (header == Section::Header) {
            break;
          }
        }
        bytecode.seekg(offset);
        goto section_functions;
        break;
      }
      case Section::Text:
        padding = "";
        WRITE(8, "TEXT:");
        padding = "  ";
        goto section_code;
        break;
    }

section_strings:
    while (true) {
      READ_INT(bytecode, ceos);
      bytecode.seekg(-4, bytecode.cur);
      if (ceos == 0xCE05)  {
        goto read_section;
      }
      static unsigned str_index = 0;
      READ_STR(bytecode, str);
      strings.push_back(str);
      WRITE(str.length() + 1, "$" << str_index++ << ": " << str);
    }

section_functions: {
      READ_INT(bytecode, fn_header);
      assert(fn_header == Section::FunctionHeader);

      READ_INT(bytecode, fn_id);
      READ_INT(bytecode, arg_count);

      std::stringstream args;
      for (int i = 0; i < arg_count; i++) {
        READ_INT(bytecode, argID);
        args << "$" << i << ": " << strings[argID];
        if (i < arg_count - 1) args << ", ";
      }

      padding = "";
      WRITE(8, strings[fn_id] << "(" << args.str() << "):");
      padding = "  ";

      while (true) {
        READ_INT(bytecode, header);
        bytecode.seekg(-4, bytecode.cur);
        if (header == Section::FunctionHeader) {
          goto section_functions;
        } else if (header == Section::Header) {
          goto read_section;
        }

        READ_INT(bytecode, opcode);
        printOpcode(bytecode, static_cast<Opcode::Type>(opcode));
      }
    }

section_code:
    while (true) {
      READ_INT(bytecode, opcode);
      printOpcode(bytecode, static_cast<Opcode::Type>(opcode));
    }
    goto read_section;
  }

  void Generator::printOpcode(std::stringstream &bytecode, Opcode::Type opcode) {
    size_t size = bytecode.str().length();
    size_t width = std::ceil(std::log10(size + 1)) + 1;

    switch (opcode) {
      case Opcode::push: {
        READ_INT(bytecode, value);
        WRITE(8, "push 0x" << std::setbase(16) << value << std::setbase(10));
        break;
      }
      case Opcode::call: {
        READ_INT(bytecode, nargs);
        WRITE(8, "call (" << nargs << ")");
        break;
      }
      case Opcode::load_string: {
        READ_INT(bytecode, stringID);
        WRITE(8, "load_string $" << stringID);
        break;
      }
      case Opcode::lookup: {
        READ_INT(bytecode, id);
        WRITE(8, "lookup $" << id << "(" << strings[id] << ")");
        break;
      }
      case Opcode::create_closure: {
        READ_INT(bytecode, fnID);
        WRITE(8, "create_closure " << functions[fnID]);
        break;
      }
      case Opcode::jmp:  {
        READ_INT(bytecode, target);
        WRITE(8, "jmp [" << ((int)bytecode.tellg() + target) << "]");
        break;
      }
      case Opcode::jz: {
        READ_INT(bytecode, target);
        WRITE(8, "jz [" << ((int)bytecode.tellg() + target) << "]");
        break;
      }
      case Opcode::push_arg: {
        READ_INT(bytecode, arg);
        WRITE(8, "push_arg $" << arg);
        break;
      }
      case Opcode::ret: {
        WRITE(4, "ret");
        break;
      }
      case Opcode::bind:
        WRITE(4, "bind");
        break;
      default:
        std::cerr << "Unhandled opcode: " << Opcode::typeName(static_cast<Opcode::Type>(opcode)) << "\n";
        throw;
    }
  }
#undef WRITE
}
<|endoftext|>