<commit_before>/*
The MIT License (MIT)

Copyright (c) 2013-2015 SRS(simple-rtmp-server)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <srs_app_thread.hpp>

#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>

namespace internal {
    ISrsThreadHandler::ISrsThreadHandler()
    {
    }

    ISrsThreadHandler::~ISrsThreadHandler()
    {
    }

    void ISrsThreadHandler::on_thread_start()
    {
    }

    int ISrsThreadHandler::on_before_cycle()
    {
        int ret = ERROR_SUCCESS;
        return ret;
    }

    int ISrsThreadHandler::on_end_cycle()
    {
        int ret = ERROR_SUCCESS;
        return ret;
    }

    void ISrsThreadHandler::on_thread_stop()
    {
    }

    SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable)
    {
        _name = name;
        handler = thread_handler;
        cycle_interval_us = interval_us;

        tid = NULL;
        loop = false;
        really_terminated = true;
        _cid = -1;
        _joinable = joinable;

        // in start(), the thread cycle method maybe stop and remove the thread itself,
        // and the thread start() is waiting for the _cid, and segment fault then.
        // @see https://github.com/simple-rtmp-server/srs/issues/110
        // thread will set _cid, callback on_thread_start(), then wait for the can_run signal.
        can_run = false;
    }

    SrsThread::~SrsThread()
    {
        stop();
    }

    int SrsThread::cid()
    {
        return _cid;
    }

    int SrsThread::start()
    {
        int ret = ERROR_SUCCESS;

        if(tid) {
            srs_info("thread %s already running.", _name);
            return ret;
        }

        if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){
            ret = ERROR_ST_CREATE_CYCLE_THREAD;
            srs_error("st_thread_create failed. ret=%d", ret);
            return ret;
        }

        // we set to loop to true for thread to run.
        loop = true;

        // wait for cid to ready, for parent thread to get the cid.
        while (_cid < 0 && loop) {
            st_usleep(10 * 1000);
        }

        // now, cycle thread can run.
        can_run = true;

        return ret;
    }

    void SrsThread::stop()
    {
        if (tid) {
            loop = false;

            // the interrupt will cause the socket to read/write error,
            // which will terminate the cycle thread.
            st_thread_interrupt(tid);

            // when joinable, wait util quit.
            if (_joinable) {
                // wait the thread to exit.
                int ret = st_thread_join(tid, NULL);
                if (ret) {
                    srs_warn("core: ignore join thread failed.");
                }
            }

            // wait the thread actually terminated.
            // sometimes the thread join return -1, for example,
            // when thread use st_recvfrom, the thread join return -1.
            // so here, we use a variable to ensure the thread stopped.
            // @remark even the thread not joinable, we must ensure the thread stopped when stop.
            while (!really_terminated) {
                st_usleep(10 * 1000);

                if (really_terminated) {
                    break;
                }
                srs_warn("core: wait thread to actually terminated");
            }

            tid = NULL;
        }
    }

    bool SrsThread::can_loop()
    {
        return loop;
    }

    void SrsThread::stop_loop()
    {
        loop = false;
    }

    void SrsThread::thread_cycle()
    {
        int ret = ERROR_SUCCESS;

        _srs_context->generate_id();
        srs_info("thread %s cycle start", _name);

        _cid = _srs_context->get_id();

        srs_assert(handler);
        handler->on_thread_start();

        // thread is running now.
        really_terminated = false;

        // wait for cid to ready, for parent thread to get the cid.
        while (!can_run && loop) {
            st_usleep(10 * 1000);
        }

        while (loop) {
            if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
                srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret);
                goto failed;
            }
            srs_info("thread %s on before cycle success");

            if ((ret = handler->cycle()) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret) && !srs_is_system_control_error(ret)) {
                    srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
                }
                goto failed;
            }
            srs_info("thread %s cycle success", _name);

            if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
                srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret);
                goto failed;
            }
            srs_info("thread %s on end cycle success", _name);

        failed:
            if (!loop) {
                break;
            }

            // to improve performance, donot sleep when interval is zero.
            // @see: https://github.com/simple-rtmp-server/srs/issues/237
            if (cycle_interval_us != 0) {
                st_usleep(cycle_interval_us);
            }
        }

        // readly terminated now.
        really_terminated = true;

        handler->on_thread_stop();
        srs_info("thread %s cycle finished", _name);

        // TODO: FIXME: when thread terminated normally, set the tid to NULL.
    }

    void* SrsThread::thread_fun(void* arg)
    {
        SrsThread* obj = (SrsThread*)arg;
        srs_assert(obj);

        obj->thread_cycle();

        st_thread_exit(NULL);

        return NULL;
    }
}

ISrsEndlessThreadHandler::ISrsEndlessThreadHandler()
{
}

ISrsEndlessThreadHandler::~ISrsEndlessThreadHandler()
{
}

void ISrsEndlessThreadHandler::on_thread_start()
{
}

int ISrsEndlessThreadHandler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsEndlessThreadHandler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsEndlessThreadHandler::on_thread_stop()
{
}

SrsEndlessThread::SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, 0, false);
}

SrsEndlessThread::~SrsEndlessThread()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsEndlessThread::start()
{
    return pthread->start();
}

int SrsEndlessThread::cycle()
{
    return handler->cycle();
}

void SrsEndlessThread::on_thread_start()
{
    handler->on_thread_start();
}

int SrsEndlessThread::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsEndlessThread::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsEndlessThread::on_thread_stop()
{
    handler->on_thread_stop();
}

ISrsOneCycleThreadHandler::ISrsOneCycleThreadHandler()
{
}

ISrsOneCycleThreadHandler::~ISrsOneCycleThreadHandler()
{
}

void ISrsOneCycleThreadHandler::on_thread_start()
{
}

int ISrsOneCycleThreadHandler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsOneCycleThreadHandler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsOneCycleThreadHandler::on_thread_stop()
{
}

SrsOneCycleThread::SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, 0, false);
}

SrsOneCycleThread::~SrsOneCycleThread()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsOneCycleThread::start()
{
    return pthread->start();
}

int SrsOneCycleThread::cycle()
{
    int ret = handler->cycle();
    pthread->stop_loop();
    return ret;
}

void SrsOneCycleThread::on_thread_start()
{
    handler->on_thread_start();
}

int SrsOneCycleThread::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsOneCycleThread::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsOneCycleThread::on_thread_stop()
{
    handler->on_thread_stop();
}

ISrsReusableThreadHandler::ISrsReusableThreadHandler()
{
}

ISrsReusableThreadHandler::~ISrsReusableThreadHandler()
{
}

void ISrsReusableThreadHandler::on_thread_start()
{
}

int ISrsReusableThreadHandler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsReusableThreadHandler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsReusableThreadHandler::on_thread_stop()
{
}

SrsReusableThread::SrsReusableThread(const char* n, ISrsReusableThreadHandler* h, int64_t interval_us)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, interval_us, true);
}

SrsReusableThread::~SrsReusableThread()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsReusableThread::start()
{
    return pthread->start();
}

void SrsReusableThread::stop()
{
    pthread->stop();
}

int SrsReusableThread::cid()
{
    return pthread->cid();
}

int SrsReusableThread::cycle()
{
    return handler->cycle();
}

void SrsReusableThread::on_thread_start()
{
    handler->on_thread_start();
}

int SrsReusableThread::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsReusableThread::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsReusableThread::on_thread_stop()
{
    handler->on_thread_stop();
}

ISrsReusableThread2Handler::ISrsReusableThread2Handler()
{
}

ISrsReusableThread2Handler::~ISrsReusableThread2Handler()
{
}

void ISrsReusableThread2Handler::on_thread_start()
{
}

int ISrsReusableThread2Handler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsReusableThread2Handler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsReusableThread2Handler::on_thread_stop()
{
}

SrsReusableThread2::SrsReusableThread2(const char* n, ISrsReusableThread2Handler* h, int64_t interval_us)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, interval_us, true);
}

SrsReusableThread2::~SrsReusableThread2()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsReusableThread2::start()
{
    return pthread->start();
}

void SrsReusableThread2::stop()
{
    pthread->stop();
}

int SrsReusableThread2::cid()
{
    return pthread->cid();
}

void SrsReusableThread2::interrupt()
{
    pthread->stop_loop();
}

bool SrsReusableThread2::interrupted()
{
    return !pthread->can_loop();
}

int SrsReusableThread2::cycle()
{
    return handler->cycle();
}

void SrsReusableThread2::on_thread_start()
{
    handler->on_thread_start();
}

int SrsReusableThread2::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsReusableThread2::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsReusableThread2::on_thread_stop()
{
    handler->on_thread_stop();
}

<commit_msg>when thread terminated normally, set the tid to NULL.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2013-2015 SRS(simple-rtmp-server)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <srs_app_thread.hpp>

#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>

namespace internal {
    ISrsThreadHandler::ISrsThreadHandler()
    {
    }

    ISrsThreadHandler::~ISrsThreadHandler()
    {
    }

    void ISrsThreadHandler::on_thread_start()
    {
    }

    int ISrsThreadHandler::on_before_cycle()
    {
        int ret = ERROR_SUCCESS;
        return ret;
    }

    int ISrsThreadHandler::on_end_cycle()
    {
        int ret = ERROR_SUCCESS;
        return ret;
    }

    void ISrsThreadHandler::on_thread_stop()
    {
    }

    SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable)
    {
        _name = name;
        handler = thread_handler;
        cycle_interval_us = interval_us;

        tid = NULL;
        loop = false;
        really_terminated = true;
        _cid = -1;
        _joinable = joinable;

        // in start(), the thread cycle method maybe stop and remove the thread itself,
        // and the thread start() is waiting for the _cid, and segment fault then.
        // @see https://github.com/simple-rtmp-server/srs/issues/110
        // thread will set _cid, callback on_thread_start(), then wait for the can_run signal.
        can_run = false;
    }

    SrsThread::~SrsThread()
    {
        stop();
    }

    int SrsThread::cid()
    {
        return _cid;
    }

    int SrsThread::start()
    {
        int ret = ERROR_SUCCESS;

        if(tid) {
            srs_info("thread %s already running.", _name);
            return ret;
        }

        if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){
            ret = ERROR_ST_CREATE_CYCLE_THREAD;
            srs_error("st_thread_create failed. ret=%d", ret);
            return ret;
        }

        // we set to loop to true for thread to run.
        loop = true;

        // wait for cid to ready, for parent thread to get the cid.
        while (_cid < 0 && loop) {
            st_usleep(10 * 1000);
        }

        // now, cycle thread can run.
        can_run = true;

        return ret;
    }

    void SrsThread::stop()
    {
        if (tid) {
            loop = false;

            // the interrupt will cause the socket to read/write error,
            // which will terminate the cycle thread.
            st_thread_interrupt(tid);

            // when joinable, wait util quit.
            if (_joinable) {
                // wait the thread to exit.
                int ret = st_thread_join(tid, NULL);
                if (ret) {
                    srs_warn("core: ignore join thread failed.");
                }
            }

            // wait the thread actually terminated.
            // sometimes the thread join return -1, for example,
            // when thread use st_recvfrom, the thread join return -1.
            // so here, we use a variable to ensure the thread stopped.
            // @remark even the thread not joinable, we must ensure the thread stopped when stop.
            while (!really_terminated) {
                st_usleep(10 * 1000);

                if (really_terminated) {
                    break;
                }
                srs_warn("core: wait thread to actually terminated");
            }

            tid = NULL;
        }
    }

    bool SrsThread::can_loop()
    {
        return loop;
    }

    void SrsThread::stop_loop()
    {
        loop = false;
    }

    void SrsThread::thread_cycle()
    {
        int ret = ERROR_SUCCESS;

        _srs_context->generate_id();
        srs_info("thread %s cycle start", _name);

        _cid = _srs_context->get_id();

        srs_assert(handler);
        handler->on_thread_start();

        // thread is running now.
        really_terminated = false;

        // wait for cid to ready, for parent thread to get the cid.
        while (!can_run && loop) {
            st_usleep(10 * 1000);
        }

        while (loop) {
            if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
                srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret);
                goto failed;
            }
            srs_info("thread %s on before cycle success");

            if ((ret = handler->cycle()) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret) && !srs_is_system_control_error(ret)) {
                    srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
                }
                goto failed;
            }
            srs_info("thread %s cycle success", _name);

            if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
                srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret);
                goto failed;
            }
            srs_info("thread %s on end cycle success", _name);

        failed:
            if (!loop) {
                break;
            }

            // to improve performance, donot sleep when interval is zero.
            // @see: https://github.com/simple-rtmp-server/srs/issues/237
            if (cycle_interval_us != 0) {
                st_usleep(cycle_interval_us);
            }
        }

        // readly terminated now.
        really_terminated = true;

        handler->on_thread_stop();
        srs_info("thread %s cycle finished", _name);

        // when thread terminated normally, set the tid to NULL.
        tid = NULL;
    }

    void* SrsThread::thread_fun(void* arg)
    {
        SrsThread* obj = (SrsThread*)arg;
        srs_assert(obj);

        obj->thread_cycle();

        st_thread_exit(NULL);

        return NULL;
    }
}

ISrsEndlessThreadHandler::ISrsEndlessThreadHandler()
{
}

ISrsEndlessThreadHandler::~ISrsEndlessThreadHandler()
{
}

void ISrsEndlessThreadHandler::on_thread_start()
{
}

int ISrsEndlessThreadHandler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsEndlessThreadHandler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsEndlessThreadHandler::on_thread_stop()
{
}

SrsEndlessThread::SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, 0, false);
}

SrsEndlessThread::~SrsEndlessThread()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsEndlessThread::start()
{
    return pthread->start();
}

int SrsEndlessThread::cycle()
{
    return handler->cycle();
}

void SrsEndlessThread::on_thread_start()
{
    handler->on_thread_start();
}

int SrsEndlessThread::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsEndlessThread::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsEndlessThread::on_thread_stop()
{
    handler->on_thread_stop();
}

ISrsOneCycleThreadHandler::ISrsOneCycleThreadHandler()
{
}

ISrsOneCycleThreadHandler::~ISrsOneCycleThreadHandler()
{
}

void ISrsOneCycleThreadHandler::on_thread_start()
{
}

int ISrsOneCycleThreadHandler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsOneCycleThreadHandler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsOneCycleThreadHandler::on_thread_stop()
{
}

SrsOneCycleThread::SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, 0, false);
}

SrsOneCycleThread::~SrsOneCycleThread()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsOneCycleThread::start()
{
    return pthread->start();
}

int SrsOneCycleThread::cycle()
{
    int ret = handler->cycle();
    pthread->stop_loop();
    return ret;
}

void SrsOneCycleThread::on_thread_start()
{
    handler->on_thread_start();
}

int SrsOneCycleThread::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsOneCycleThread::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsOneCycleThread::on_thread_stop()
{
    handler->on_thread_stop();
}

ISrsReusableThreadHandler::ISrsReusableThreadHandler()
{
}

ISrsReusableThreadHandler::~ISrsReusableThreadHandler()
{
}

void ISrsReusableThreadHandler::on_thread_start()
{
}

int ISrsReusableThreadHandler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsReusableThreadHandler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsReusableThreadHandler::on_thread_stop()
{
}

SrsReusableThread::SrsReusableThread(const char* n, ISrsReusableThreadHandler* h, int64_t interval_us)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, interval_us, true);
}

SrsReusableThread::~SrsReusableThread()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsReusableThread::start()
{
    return pthread->start();
}

void SrsReusableThread::stop()
{
    pthread->stop();
}

int SrsReusableThread::cid()
{
    return pthread->cid();
}

int SrsReusableThread::cycle()
{
    return handler->cycle();
}

void SrsReusableThread::on_thread_start()
{
    handler->on_thread_start();
}

int SrsReusableThread::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsReusableThread::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsReusableThread::on_thread_stop()
{
    handler->on_thread_stop();
}

ISrsReusableThread2Handler::ISrsReusableThread2Handler()
{
}

ISrsReusableThread2Handler::~ISrsReusableThread2Handler()
{
}

void ISrsReusableThread2Handler::on_thread_start()
{
}

int ISrsReusableThread2Handler::on_before_cycle()
{
    return ERROR_SUCCESS;
}

int ISrsReusableThread2Handler::on_end_cycle()
{
    return ERROR_SUCCESS;
}

void ISrsReusableThread2Handler::on_thread_stop()
{
}

SrsReusableThread2::SrsReusableThread2(const char* n, ISrsReusableThread2Handler* h, int64_t interval_us)
{
    handler = h;
    pthread = new internal::SrsThread(n, this, interval_us, true);
}

SrsReusableThread2::~SrsReusableThread2()
{
    pthread->stop();
    srs_freep(pthread);
}

int SrsReusableThread2::start()
{
    return pthread->start();
}

void SrsReusableThread2::stop()
{
    pthread->stop();
}

int SrsReusableThread2::cid()
{
    return pthread->cid();
}

void SrsReusableThread2::interrupt()
{
    pthread->stop_loop();
}

bool SrsReusableThread2::interrupted()
{
    return !pthread->can_loop();
}

int SrsReusableThread2::cycle()
{
    return handler->cycle();
}

void SrsReusableThread2::on_thread_start()
{
    handler->on_thread_start();
}

int SrsReusableThread2::on_before_cycle()
{
    return handler->on_before_cycle();
}

int SrsReusableThread2::on_end_cycle()
{
    return handler->on_end_cycle();
}

void SrsReusableThread2::on_thread_stop()
{
    handler->on_thread_stop();
}

<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "test/codec_factory.h"
#include "test/decode_test_driver.h"
#include "test/ivf_video_source.h"
#include "test/md5_helper.h"
#include "test/util.h"
#include "test/webm_video_source.h"
#include "vpx_ports/vpx_timer.h"
#include "./vpx_version.h"

using std::tr1::make_tuple;

namespace {

#define VIDEO_NAME 0
#define THREADS 1

const double kUsecsInSec = 1000000.0;

/*
 DecodePerfTest takes a tuple of filename + number of threads to decode with
 */
typedef std::tr1::tuple<const char *const, unsigned> decode_perf_param_t;

const decode_perf_param_t kVP9DecodePerfVectors[] = {
  make_tuple("vp90-2-bbb_426x240_tile_1x1_180kbps.webm", 1),
  make_tuple("vp90-2-bbb_640x360_tile_1x2_337kbps.webm", 2),
  make_tuple("vp90-2-bbb_854x480_tile_1x2_651kbps.webm", 2),
  make_tuple("vp90-2-bbb_1280x720_tile_1x4_1310kbps.webm", 4),
  make_tuple("vp90-2-bbb_1920x1080_tile_1x1_2581kbps.webm", 1),
  make_tuple("vp90-2-bbb_1920x1080_tile_1x4_2586kbps.webm", 4),
  make_tuple("vp90-2-bbb_1920x1080_tile_1x4_fpm_2304kbps.webm", 4),
  make_tuple("vp90-2-sintel_426x182_tile_1x1_171kbps.webm", 1),
  make_tuple("vp90-2-sintel_640x272_tile_1x2_318kbps.webm", 2),
  make_tuple("vp90-2-sintel_854x364_tile_1x2_621kbps.webm", 2),
  make_tuple("vp90-2-sintel_1280x546_tile_1x4_1257kbps.webm", 4),
  make_tuple("vp90-2-sintel_1920x818_tile_1x4_fpm_2279kbps.webm", 4),
  make_tuple("vp90-2-tos_426x178_tile_1x1_181kbps.webm", 1),
  make_tuple("vp90-2-tos_640x266_tile_1x2_336kbps.webm", 2),
  make_tuple("vp90-2-tos_854x356_tile_1x2_656kbps.webm", 2),
  make_tuple("vp90-2-tos_1280x534_tile_1x4_1306kbps.webm", 4),
  make_tuple("vp90-2-tos_1920x800_tile_1x4_fpm_2335kbps.webm", 4),
};

/*
 In order to reflect real world performance as much as possible, Perf tests
 *DO NOT* do any correctness checks. Please run them alongside correctness
 tests to ensure proper codec integrity. Furthermore, in this test we
 deliberately limit the amount of system calls we make to avoid OS
 preemption.

 TODO(joshualitt) create a more detailed perf measurement test to collect
   power/temp/min max frame decode times/etc
 */

class DecodePerfTest : public ::testing::TestWithParam<decode_perf_param_t> {
};

TEST_P(DecodePerfTest, PerfTest) {
  const char *const video_name = GET_PARAM(VIDEO_NAME);
  const unsigned threads = GET_PARAM(THREADS);

  libvpx_test::WebMVideoSource video(video_name);
  video.Init();

  vpx_codec_dec_cfg_t cfg = {0};
  cfg.threads = threads;
  libvpx_test::VP9Decoder decoder(cfg, 0);

  vpx_usec_timer t;
  vpx_usec_timer_start(&t);

  for (video.Begin(); video.cxdata() != NULL; video.Next()) {
    decoder.DecodeFrame(video.cxdata(), video.frame_size());
  }

  vpx_usec_timer_mark(&t);
  const double elapsed_secs = double(vpx_usec_timer_elapsed(&t))
                              / kUsecsInSec;
  const unsigned frames = video.frame_number();
  const double fps = double(frames) / elapsed_secs;

  printf("{\n");
  printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
  printf("\t\"videoName\" : \"%s\",\n", video_name);
  printf("\t\"threadCount\" : %u,\n", threads);
  printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
  printf("\t\"totalFrames\" : %u,\n", frames);
  printf("\t\"framesPerSecond\" : %f\n", fps);
  printf("}\n");
}

INSTANTIATE_TEST_CASE_P(VP9, DecodePerfTest,
                        ::testing::ValuesIn(kVP9DecodePerfVectors));

}  // namespace
<commit_msg>test/decode_perf_test: fix msvc build<commit_after>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "test/codec_factory.h"
#include "test/decode_test_driver.h"
#include "test/ivf_video_source.h"
#include "test/md5_helper.h"
#include "test/util.h"
#include "test/webm_video_source.h"
#include "vpx_ports/vpx_timer.h"
#include "./vpx_version.h"

using std::tr1::make_tuple;

namespace {

#define VIDEO_NAME 0
#define THREADS 1

const double kUsecsInSec = 1000000.0;

/*
 DecodePerfTest takes a tuple of filename + number of threads to decode with
 */
typedef std::tr1::tuple<const char *, unsigned> decode_perf_param_t;

const decode_perf_param_t kVP9DecodePerfVectors[] = {
  make_tuple("vp90-2-bbb_426x240_tile_1x1_180kbps.webm", 1),
  make_tuple("vp90-2-bbb_640x360_tile_1x2_337kbps.webm", 2),
  make_tuple("vp90-2-bbb_854x480_tile_1x2_651kbps.webm", 2),
  make_tuple("vp90-2-bbb_1280x720_tile_1x4_1310kbps.webm", 4),
  make_tuple("vp90-2-bbb_1920x1080_tile_1x1_2581kbps.webm", 1),
  make_tuple("vp90-2-bbb_1920x1080_tile_1x4_2586kbps.webm", 4),
  make_tuple("vp90-2-bbb_1920x1080_tile_1x4_fpm_2304kbps.webm", 4),
  make_tuple("vp90-2-sintel_426x182_tile_1x1_171kbps.webm", 1),
  make_tuple("vp90-2-sintel_640x272_tile_1x2_318kbps.webm", 2),
  make_tuple("vp90-2-sintel_854x364_tile_1x2_621kbps.webm", 2),
  make_tuple("vp90-2-sintel_1280x546_tile_1x4_1257kbps.webm", 4),
  make_tuple("vp90-2-sintel_1920x818_tile_1x4_fpm_2279kbps.webm", 4),
  make_tuple("vp90-2-tos_426x178_tile_1x1_181kbps.webm", 1),
  make_tuple("vp90-2-tos_640x266_tile_1x2_336kbps.webm", 2),
  make_tuple("vp90-2-tos_854x356_tile_1x2_656kbps.webm", 2),
  make_tuple("vp90-2-tos_1280x534_tile_1x4_1306kbps.webm", 4),
  make_tuple("vp90-2-tos_1920x800_tile_1x4_fpm_2335kbps.webm", 4),
};

/*
 In order to reflect real world performance as much as possible, Perf tests
 *DO NOT* do any correctness checks. Please run them alongside correctness
 tests to ensure proper codec integrity. Furthermore, in this test we
 deliberately limit the amount of system calls we make to avoid OS
 preemption.

 TODO(joshualitt) create a more detailed perf measurement test to collect
   power/temp/min max frame decode times/etc
 */

class DecodePerfTest : public ::testing::TestWithParam<decode_perf_param_t> {
};

TEST_P(DecodePerfTest, PerfTest) {
  const char *const video_name = GET_PARAM(VIDEO_NAME);
  const unsigned threads = GET_PARAM(THREADS);

  libvpx_test::WebMVideoSource video(video_name);
  video.Init();

  vpx_codec_dec_cfg_t cfg = {0};
  cfg.threads = threads;
  libvpx_test::VP9Decoder decoder(cfg, 0);

  vpx_usec_timer t;
  vpx_usec_timer_start(&t);

  for (video.Begin(); video.cxdata() != NULL; video.Next()) {
    decoder.DecodeFrame(video.cxdata(), video.frame_size());
  }

  vpx_usec_timer_mark(&t);
  const double elapsed_secs = double(vpx_usec_timer_elapsed(&t))
                              / kUsecsInSec;
  const unsigned frames = video.frame_number();
  const double fps = double(frames) / elapsed_secs;

  printf("{\n");
  printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
  printf("\t\"videoName\" : \"%s\",\n", video_name);
  printf("\t\"threadCount\" : %u,\n", threads);
  printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
  printf("\t\"totalFrames\" : %u,\n", frames);
  printf("\t\"framesPerSecond\" : %f\n", fps);
  printf("}\n");
}

INSTANTIATE_TEST_CASE_P(VP9, DecodePerfTest,
                        ::testing::ValuesIn(kVP9DecodePerfVectors));

}  // namespace
<|endoftext|>


<commit_before>// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-443271.
//
// This file is part of the GLVis visualization tool and library. For more
// information and source code availability see https://glvis.org.
//
// GLVis is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef GLVIS_SDL_X11_HPP
#define GLVIS_SDL_X11_HPP

#ifdef SDL_VIDEO_DRIVER_X11

#include "sdl_helper.hpp"
#include "gl/platform_gl.hpp"
#include <poll.h>

#include <unistd.h>    // pipe, fcntl, write
#include <fcntl.h>     // fcntl
#include <cerrno>      // errno, EAGAIN
#include <cstdio>      // perror
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
#include <X11/extensions/XInput2.h>
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2

class SdlX11Platform final : public SdlNativePlatform
{
public:
   SdlX11Platform(Display* xdisplay, Window xwindow)
      : disp(xdisplay), wnd(xwindow)
   {
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
      // Disable XInput extension events since they are generated even outside
      // the GLVis window.
      Window root_win = DefaultRootWindow(disp);
      unsigned char mask[4] = {0,0,0,0};
      XIEventMask event_mask;
      event_mask.deviceid = XIAllMasterDevices;
      event_mask.mask_len = sizeof(mask);
      event_mask.mask = mask;
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
      const char Xi_lib[] = SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2;
#else
      const char Xi_lib[] = "libXi.so";
#endif
      typedef int (*XISelectEvents_ptr)(Display *, Window, XIEventMask *, int);
      XISelectEvents_ptr XISelectEvents_ = NULL;
      void *lib = SDL_LoadObject(Xi_lib);
      if (lib != NULL)
      {
         XISelectEvents_ =
            (XISelectEvents_ptr)SDL_LoadFunction(lib, "XISelectEvents");
      }
      if (XISelectEvents_ == NULL)
      {
         cerr << "Error accessing XISelectEvents!" << endl;
         exit(EXIT_FAILURE);
      }
      if (XISelectEvents_(disp, root_win, &event_mask, 1) != Success)
      {
         cerr << "Failed to disable XInput on the default root window!" << endl;
      }
      if (XISelectEvents_(disp, wnd, &event_mask, 1) != Success)
      {
         cerr << "Failed to disable XInput on the current window!" << endl;
      }
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
      SDL_UnloadObject(lib);
#endif
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2

      // Create pipe for external events
      if (pipe(event_pfd) == -1)
      {
         perror("pipe()");
         exit(EXIT_FAILURE);
      }
      int flag = fcntl(event_pfd[0], F_GETFL);
      fcntl(event_pfd[0], F_SETFL, flag | O_NONBLOCK);
   }

   ~SdlX11Platform()
   {
       close(event_pfd[0]);
       close(event_pfd[1]);
   }

   void WaitEvent()
   {
      int nstr, nfd = 2;
      struct pollfd pfd[2];

      pfd[0].fd     = ConnectionNumber(disp);
      pfd[0].events = POLLIN;
      pfd[0].revents = 0;

      pfd[1].fd     = event_pfd[0];
      pfd[1].events = POLLIN;
      pfd[1].revents = 0;

      do
      {
         nstr = poll(pfd, nfd, -1);
      }
      while (nstr == -1 && errno == EINTR);

      if (nstr == -1) { perror("poll()"); }

      int n = 0;
      // Read out the pending GLVisCommand-sent events, if any
      do
      {
          char c[10];
          n = read(event_pfd[0], c, 10);
      }
      while (n > 0);
   }
   void SendEvent()
   {
       char c = 's';
       if (write(event_pfd[1], &c, 1) != 1)
       {
         perror("write()");
         exit(EXIT_FAILURE);
       }
   }

private:
   Display* disp;
   Window wnd;
   int event_pfd[2]; // pfd[0] -- reading, pfd[1] -- writing
};

#endif // SDL_VIDEO_DRIVER_X11

#endif // GLVIS_SDL_X11_HPP
<commit_msg>make style<commit_after>// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-443271.
//
// This file is part of the GLVis visualization tool and library. For more
// information and source code availability see https://glvis.org.
//
// GLVis is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef GLVIS_SDL_X11_HPP
#define GLVIS_SDL_X11_HPP

#ifdef SDL_VIDEO_DRIVER_X11

#include "sdl_helper.hpp"
#include "gl/platform_gl.hpp"
#include <poll.h>

#include <unistd.h>    // pipe, fcntl, write
#include <fcntl.h>     // fcntl
#include <cerrno>      // errno, EAGAIN
#include <cstdio>      // perror
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
#include <X11/extensions/XInput2.h>
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2

class SdlX11Platform final : public SdlNativePlatform
{
public:
   SdlX11Platform(Display* xdisplay, Window xwindow)
      : disp(xdisplay), wnd(xwindow)
   {
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
      // Disable XInput extension events since they are generated even outside
      // the GLVis window.
      Window root_win = DefaultRootWindow(disp);
      unsigned char mask[4] = {0,0,0,0};
      XIEventMask event_mask;
      event_mask.deviceid = XIAllMasterDevices;
      event_mask.mask_len = sizeof(mask);
      event_mask.mask = mask;
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
      const char Xi_lib[] = SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2;
#else
      const char Xi_lib[] = "libXi.so";
#endif
      typedef int (*XISelectEvents_ptr)(Display *, Window, XIEventMask *, int);
      XISelectEvents_ptr XISelectEvents_ = NULL;
      void *lib = SDL_LoadObject(Xi_lib);
      if (lib != NULL)
      {
         XISelectEvents_ =
            (XISelectEvents_ptr)SDL_LoadFunction(lib, "XISelectEvents");
      }
      if (XISelectEvents_ == NULL)
      {
         cerr << "Error accessing XISelectEvents!" << endl;
         exit(EXIT_FAILURE);
      }
      if (XISelectEvents_(disp, root_win, &event_mask, 1) != Success)
      {
         cerr << "Failed to disable XInput on the default root window!" << endl;
      }
      if (XISelectEvents_(disp, wnd, &event_mask, 1) != Success)
      {
         cerr << "Failed to disable XInput on the current window!" << endl;
      }
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
      SDL_UnloadObject(lib);
#endif
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2

      // Create pipe for external events
      if (pipe(event_pfd) == -1)
      {
         perror("pipe()");
         exit(EXIT_FAILURE);
      }
      int flag = fcntl(event_pfd[0], F_GETFL);
      fcntl(event_pfd[0], F_SETFL, flag | O_NONBLOCK);
   }

   ~SdlX11Platform()
   {
      close(event_pfd[0]);
      close(event_pfd[1]);
   }

   void WaitEvent()
   {
      int nstr, nfd = 2;
      struct pollfd pfd[2];

      pfd[0].fd     = ConnectionNumber(disp);
      pfd[0].events = POLLIN;
      pfd[0].revents = 0;

      pfd[1].fd     = event_pfd[0];
      pfd[1].events = POLLIN;
      pfd[1].revents = 0;

      do
      {
         nstr = poll(pfd, nfd, -1);
      }
      while (nstr == -1 && errno == EINTR);

      if (nstr == -1) { perror("poll()"); }

      int n = 0;
      // Read out the pending GLVisCommand-sent events, if any
      do
      {
         char c[10];
         n = read(event_pfd[0], c, 10);
      }
      while (n > 0);
   }
   void SendEvent()
   {
      char c = 's';
      if (write(event_pfd[1], &c, 1) != 1)
      {
         perror("write()");
         exit(EXIT_FAILURE);
      }
   }

private:
   Display* disp;
   Window wnd;
   int event_pfd[2]; // pfd[0] -- reading, pfd[1] -- writing
};

#endif // SDL_VIDEO_DRIVER_X11

#endif // GLVIS_SDL_X11_HPP
<|endoftext|>


<commit_before>/**
 * @file
 * Lists and hashes
 *
 */

#include "angort.h"
#include "hash.h"
#include "opcodes.h"

using namespace angort;

namespace angort {
struct RevStdComparator : public ArrayListComparator<Value> {
    Angort *ang;
    RevStdComparator(Angort *a){
        ang = a;
    }
    virtual int compare(const Value *a, const Value *b){
        // binop isn't const, sadly.
        ang->binop(const_cast<Value *>(b),
                   const_cast<Value *>(a),OP_CMP);
        return ang->popInt();
    }
};

struct FuncComparator : public ArrayListComparator<Value> {
    Value *func;
    Angort *ang;

    FuncComparator(Angort *a,Value *f){
        ang = a;
        func = f;
    }
    virtual int compare(const Value *a, const Value *b){
        ang->pushval()->copy(a);
        ang->pushval()->copy(b);
        ang->runValue(func);
        return ang->popInt();
    }
};

}

%name coll

%word dumplist (list --) Dump a list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());

    for(int i=0;i<list->count();i++){
        const StringBuffer& s = list->get(i)->toString();
        printf("%d: %s\n",i,s.get());
    }
}

%word get (key coll --) get an item from a list or hash
{
    Value *c = a->popval();
    Value *keyAndResult = a->stack.peekptr();
    Value v;
    c->t->getValue(c,keyAndResult,&v);
    keyAndResult->copy(&v); // copy into the key's slot
}
%word set (val key coll --) put an item into a list or hash
{
    Value *c = a->popval();
    Value *k = a->popval();
    Value *v = a->popval();
    c->t->setValue(c,k,v);
}
%word len (list --) get length of list, hash or string
{
    Value *c = a->stack.peekptr();
    int ct = c->t->getCount(c);
    Types::tInteger->set(c,ct);
}

%word remove (idx list -- item) remove an item by index, returning it
{
    Value *c = a->popval();
    Value *keyAndResult = a->stack.peekptr();
    Value v;
    c->t->removeAndReturn(c,keyAndResult,&v);
    keyAndResult->copy(&v); // copy into the key's slot
}

%word shift (list -- item) remove and return the first item of the list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->pushval();

    v->copy(list->get(0));
    list->remove(0);
}

%word unshift (item list --) prepend an item to a list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->popval();
    list->insert(0)->copy(v);
}

%word pop (list -- item) pop an item from the end of the list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->pushval();
    Value *src = list->get(list->count()-1);
    v->copy(src);
    list->remove(list->count()-1);
}

%word push (item list --) append an item to a list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->popval();
    list->append()->copy(v);
}

%word map (iter func -- list) apply a function to an iterable, giving a list
{
    Value func;
    func.copy(a->popval()); // need a local copy
    Value *iterable = a->popval();

    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);
    ArrayList<Value> *list = Types::tList->set(a->pushval());

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current());
        a->runValue(&func);
        Value *v = list->append();
        v->copy(a->popval());
    }
    delete iter;
}

%word reduce (start iter func -- result) perform a (left) fold or reduce on an iterable
{
    Value func;
    func.copy(a->popval()); // need a local copy

    Value *iterable = a->popval();
    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);

    // accumulator is already on the stack

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current()); // stack the iterator on top of the accum
        a->runValue(&func); // run the function, leaving the new accumulator
    }
    delete iter;
}

%word filter (iter func -- list) filter an iterable with a boolean function
{
    Value func;
    func.copy(a->popval()); // need a local copy
    Value *iterable = a->popval();

    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);
    ArrayList<Value> *list = Types::tList->set(a->pushval());

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current());
        a->runValue(&func);
        if(a->popval()->toInt()){
            Value *v = list->append();
            v->copy(iter->current());
        }
    }
    delete iter;
}

%word in (item iterable -- bool) return if item is in list or hash keys
{
    Value *iterable = a->popval();
    Value *item = a->popval();

    a->pushInt(iterable->t->isIn(iterable,item)?true:false);
}

%word slice (start len iterable -- iterable) produce a slice of a string or list
{
    Value *iterable = a->popval();
    int len = a->popInt();
    int start = a->popInt();

    Value *res = a->pushval();
    iterable->t->slice(res,iterable,start,len);
}

%word clone (in -- out) construct a shallow copy of a collection
{
    Value *v = a->stack.peekptr();
    v->t->clone(v,v,false);
}

%word deepclone (in -- out) produce a deep copy of a value
{
    Value *v = a->stack.peekptr();
    v->t->clone(v,v,true);

}

struct StdComparator : public ArrayListComparator<Value> {
    Angort *ang;
    StdComparator(Angort *a){
        ang = a;
    }
    virtual int compare(const Value *a, const Value *b){
        // binop isn't const, sadly.
        ang->binop(const_cast<Value *>(a),
                   const_cast<Value *>(b),OP_CMP);
        return ang->popInt();
    }
};

%word sort (in --) sort a list in place using default comparator
{
    Value listv;
    // need copy because comparators use the stack
    listv.copy(a->popval());
    ArrayList<Value> *list = Types::tList->get(&listv);

    StdComparator cmp(a);
    list->sort(&cmp);
}

%word rsort (in --) reverse sort a list in place using default comparator
{
    Value listv;
    // need copy because comparators use the stack
    listv.copy(a->popval());
    ArrayList<Value> *list = Types::tList->get(&listv);

    RevStdComparator cmp(a);
    list->sort(&cmp);
}

%word fsort (in func --) sort a list in place using function comparator
{
    Value func,listv;

    // need copies because comparators use the stack
    func.copy(a->popval());
    listv.copy(a->popval());

    ArrayList<Value> *list = Types::tList->get(&listv);

    FuncComparator cmp(a,&func);
    list->sort(&cmp);
}

%word all (in func --) true if the function returns true for all items
{
    int rv=1; // true by default

    Value func;
    func.copy(a->popval()); // need a local copy

    Value *iterable = a->popval();
    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current()); // stack the iterator on top of the accum
        a->runValue(&func); // run the function
        if(!a->popInt()){
            rv = 0;
            break;
        }
    }
    a->pushInt(rv);
    delete iter;
}

%word any (in func --) true if the function returns true for all items
{
    int rv=0; // false by default

    Value func;
    func.copy(a->popval()); // need a local copy

    Value *iterable = a->popval();
    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current()); // stack the iterator on top of the accum
        a->runValue(&func); // run the function
        if(a->popInt()){
            rv = 1;
            break;
        }
    }
    a->pushInt(rv);
    delete iter;
}

%word zipWith (in1 in2 func -- out) apply binary func to pairs of items in list
{
    Value *p[3];
    a->popParams(p,"llc");
    Iterator<Value *> *iter1 = p[0]->t->makeIterator(p[0]);
    Iterator<Value *> *iter2 = p[1]->t->makeIterator(p[1]);

    Value func;
    func.copy(p[2]); // need a local copy

    ArrayList<Value> *list = Types::tList->set(a->pushval());

    for(iter1->first(),iter2->first();
        !(iter1->isDone() || iter2->isDone());
        iter1->next(),iter2->next()){

        a->pushval()->copy(iter1->current());
        a->pushval()->copy(iter2->current());
        a->runValue(&func);

        list->append()->copy(a->popval());
    }

    delete iter1;
    delete iter2;

}
<commit_msg>intercalate (list str -- str)<commit_after>/**
 * @file
 * Lists and hashes
 *
 */

#include "angort.h"
#include "hash.h"
#include "opcodes.h"

using namespace angort;

namespace angort {
struct RevStdComparator : public ArrayListComparator<Value> {
    Angort *ang;
    RevStdComparator(Angort *a){
        ang = a;
    }
    virtual int compare(const Value *a, const Value *b){
        // binop isn't const, sadly.
        ang->binop(const_cast<Value *>(b),
                   const_cast<Value *>(a),OP_CMP);
        return ang->popInt();
    }
};

struct FuncComparator : public ArrayListComparator<Value> {
    Value *func;
    Angort *ang;

    FuncComparator(Angort *a,Value *f){
        ang = a;
        func = f;
    }
    virtual int compare(const Value *a, const Value *b){
        ang->pushval()->copy(a);
        ang->pushval()->copy(b);
        ang->runValue(func);
        return ang->popInt();
    }
};

}

%name coll

%word dumplist (list --) Dump a list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());

    for(int i=0;i<list->count();i++){
        const StringBuffer& s = list->get(i)->toString();
        printf("%d: %s\n",i,s.get());
    }
}

%word get (key coll --) get an item from a list or hash
{
    Value *c = a->popval();
    Value *keyAndResult = a->stack.peekptr();
    Value v;
    c->t->getValue(c,keyAndResult,&v);
    keyAndResult->copy(&v); // copy into the key's slot
}
%word set (val key coll --) put an item into a list or hash
{
    Value *c = a->popval();
    Value *k = a->popval();
    Value *v = a->popval();
    c->t->setValue(c,k,v);
}
%word len (list --) get length of list, hash or string
{
    Value *c = a->stack.peekptr();
    int ct = c->t->getCount(c);
    Types::tInteger->set(c,ct);
}

%word remove (idx list -- item) remove an item by index, returning it
{
    Value *c = a->popval();
    Value *keyAndResult = a->stack.peekptr();
    Value v;
    c->t->removeAndReturn(c,keyAndResult,&v);
    keyAndResult->copy(&v); // copy into the key's slot
}

%word shift (list -- item) remove and return the first item of the list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->pushval();

    v->copy(list->get(0));
    list->remove(0);
}

%word unshift (item list --) prepend an item to a list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->popval();
    list->insert(0)->copy(v);
}

%word pop (list -- item) pop an item from the end of the list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->pushval();
    Value *src = list->get(list->count()-1);
    v->copy(src);
    list->remove(list->count()-1);
}

%word push (item list --) append an item to a list
{
    ArrayList<Value> *list = Types::tList->get(a->popval());
    Value *v = a->popval();
    list->append()->copy(v);
}

%word map (iter func -- list) apply a function to an iterable, giving a list
{
    Value func;
    func.copy(a->popval()); // need a local copy
    Value *iterable = a->popval();

    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);
    ArrayList<Value> *list = Types::tList->set(a->pushval());

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current());
        a->runValue(&func);
        Value *v = list->append();
        v->copy(a->popval());
    }
    delete iter;
}

%word reduce (start iter func -- result) perform a (left) fold or reduce on an iterable
{
    Value func;
    func.copy(a->popval()); // need a local copy

    Value *iterable = a->popval();
    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);

    // accumulator is already on the stack

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current()); // stack the iterator on top of the accum
        a->runValue(&func); // run the function, leaving the new accumulator
    }
    delete iter;
}

%word filter (iter func -- list) filter an iterable with a boolean function
{
    Value func;
    func.copy(a->popval()); // need a local copy
    Value *iterable = a->popval();

    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);
    ArrayList<Value> *list = Types::tList->set(a->pushval());

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current());
        a->runValue(&func);
        if(a->popval()->toInt()){
            Value *v = list->append();
            v->copy(iter->current());
        }
    }
    delete iter;
}

%word in (item iterable -- bool) return if item is in list or hash keys
{
    Value *iterable = a->popval();
    Value *item = a->popval();

    a->pushInt(iterable->t->isIn(iterable,item)?true:false);
}

%word slice (start len iterable -- iterable) produce a slice of a string or list
{
    Value *iterable = a->popval();
    int len = a->popInt();
    int start = a->popInt();

    Value *res = a->pushval();
    iterable->t->slice(res,iterable,start,len);
}

%word clone (in -- out) construct a shallow copy of a collection
{
    Value *v = a->stack.peekptr();
    v->t->clone(v,v,false);
}

%word deepclone (in -- out) produce a deep copy of a value
{
    Value *v = a->stack.peekptr();
    v->t->clone(v,v,true);

}

struct StdComparator : public ArrayListComparator<Value> {
    Angort *ang;
    StdComparator(Angort *a){
        ang = a;
    }
    virtual int compare(const Value *a, const Value *b){
        // binop isn't const, sadly.
        ang->binop(const_cast<Value *>(a),
                   const_cast<Value *>(b),OP_CMP);
        return ang->popInt();
    }
};

%word sort (in --) sort a list in place using default comparator
{
    Value listv;
    // need copy because comparators use the stack
    listv.copy(a->popval());
    ArrayList<Value> *list = Types::tList->get(&listv);

    StdComparator cmp(a);
    list->sort(&cmp);
}

%word rsort (in --) reverse sort a list in place using default comparator
{
    Value listv;
    // need copy because comparators use the stack
    listv.copy(a->popval());
    ArrayList<Value> *list = Types::tList->get(&listv);

    RevStdComparator cmp(a);
    list->sort(&cmp);
}

%word fsort (in func --) sort a list in place using function comparator
{
    Value func,listv;

    // need copies because comparators use the stack
    func.copy(a->popval());
    listv.copy(a->popval());

    ArrayList<Value> *list = Types::tList->get(&listv);

    FuncComparator cmp(a,&func);
    list->sort(&cmp);
}

%word all (in func --) true if the function returns true for all items
{
    int rv=1; // true by default

    Value func;
    func.copy(a->popval()); // need a local copy

    Value *iterable = a->popval();
    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current()); // stack the iterator on top of the accum
        a->runValue(&func); // run the function
        if(!a->popInt()){
            rv = 0;
            break;
        }
    }
    a->pushInt(rv);
    delete iter;
}

%word any (in func --) true if the function returns true for all items
{
    int rv=0; // false by default

    Value func;
    func.copy(a->popval()); // need a local copy

    Value *iterable = a->popval();
    Iterator<Value *> *iter = iterable->t->makeIterator(iterable);

    for(iter->first();!iter->isDone();iter->next()){
        a->pushval()->copy(iter->current()); // stack the iterator on top of the accum
        a->runValue(&func); // run the function
        if(a->popInt()){
            rv = 1;
            break;
        }
    }
    a->pushInt(rv);
    delete iter;
}

%word zipWith (in1 in2 func -- out) apply binary func to pairs of items in list
{
    Value *p[3];
    a->popParams(p,"llc");
    Iterator<Value *> *iter1 = p[0]->t->makeIterator(p[0]);
    Iterator<Value *> *iter2 = p[1]->t->makeIterator(p[1]);

    Value func;
    func.copy(p[2]); // need a local copy

    ArrayList<Value> *list = Types::tList->set(a->pushval());

    for(iter1->first(),iter2->first();
        !(iter1->isDone() || iter2->isDone());
        iter1->next(),iter2->next()){

        a->pushval()->copy(iter1->current());
        a->pushval()->copy(iter2->current());
        a->runValue(&func);

        list->append()->copy(a->popval());
    }

    delete iter1;
    delete iter2;

}

%word intercalate (iter string -- string) turn elements of collection into string and intercalate with a separator
{
    Value *p[2];
    a->popParams(p,"vv"); // can be any type

    Value *v = p[0];
    Iterator<Value *> *iter = v->t->makeIterator(v);
    const StringBuffer& s = p[1]->toString();
    const char *sep = s.get();
    int seplen = strlen(sep);

    int count = v->t->getCount(v);

    // first pass to get the lengths
    int len=0;
    int n=0;
    for(n=0,iter->first();!iter->isDone();iter->next(),n++){
        len += strlen(iter->current()->toString().get());
        if(n!=count-1)
            len += seplen;
    }

    // allocate the result in a new string value on the stack
    char *out = Types::tString->allocate(a->pushval(),len,Types::tString);
    // second pass to write the value
    char *snark=out;
    *out = 0;
    for(n=0,iter->first();!iter->isDone();iter->next(),n++){
        const StringBuffer& b = iter->current()->toString();
        strcpy(out,b.get());
        out += strlen(b.get());
        if(n!=count-1){
            strcpy(out,sep);
            out+=seplen;
        }
    }
}

<|endoftext|>


<commit_before>
#include <QGridLayout>
#include <QBoxLayout>
#include <QButtonGroup>
#include <QGroupBox>
#include <QSignalMapper>
#include <QSplitter>

#include "wd.h"
#include "font.h"
#include "cmd.h"
#include "form.h"
#include "pane.h"
#include "child.h"
#include "layout.h"

#include "button.h"
#include "checkbox.h"
#include "combobox.h"
#include "dateedit.h"
#include "dspinbox.h"
#include "dummy.h"
#include "edit.h"
#include "editm.h"
#include "image.h"
#include "isigraph.h"
#include "listbox.h"
#ifdef QT_OPENGL
#include "opengl.h"
#endif
#include "progressbar.h"
#include "radiobutton.h"
#include "slider.h"
#include "spinbox.h"
#include "static.h"
#include "statusbar.h"
#include "table.h"
#include "tabs.h"
#include "timeedit.h"
#include "toolbar.h"
#ifdef QT_WEBKIT
#include "webview.h"
#endif

extern Font *fontdef;

// ---------------------------------------------------------------------
Pane::Pane(int n,Form *f) : QWidget(f)
{
  pform=f;
  buttongroup=0;
  child=0;
  groupboxw=0;
  layout=0;
  sizew=sizeh=0;
  if (n==1) {
    bin("v");
    layout->bin->setContentsMargins(0,0,0,0);
    layout->bin->setSpacing(0);
  }
}

// ---------------------------------------------------------------------
// return if child valid
bool Pane::addchild(string n,string c,string p)
{
  if (!layout)
    bin("v");
  if (c=="button")
    child=(Child *) new Button(n,p,pform,this);
  else if (c=="checkbox")
    child=(Child *) new CheckBox(n,p,pform,this);
  else if (c=="dateedit")
    child=(Child *) new DateEdit(n,p,pform,this);
  else if (c=="dspinbox")
    child=(Child *) new DSpinBox(n,p,pform,this);
  else if (c=="edit")
    child=(Child *) new Edit(n,p,pform,this);
  else if (c=="editm")
    child=(Child *) new Editm(n,p,pform,this);
  else if (c=="combobox")
    child=(Child *) new ComboBox(n,"edit " + p,pform,this);
  else if (c=="combolist")
    child=(Child *) new ComboBox(n,p,pform,this);
  else if (c=="image")
    child=(Child *) new Image(n,p,pform,this);
  else if (c=="isigraph")
    child=(Child *) new Isigraph(n,p,pform,this);
  else if (c=="listbox")
    child=(Child *) new ListBox(n,p,pform,this);
#ifdef QT_OPENGL
  else if (c=="opengl")
    child=(Child *) new Opengl(n,p,pform,this);
#endif
  else if (c=="progressbar")
    child=(Child *) new ProgressBar(n,p,pform,this);
  else if (c=="radiobutton")
    child=(Child *) new RadioButton(n,p,pform,this);
  else if (c=="slider")
    child=(Child *) new Slider(n,p,pform,this);
  else if (c=="spinbox")
    child=(Child *) new SpinBox(n,p,pform,this);
  else if (c=="static")
    child=(Child *) new Static(n,p,pform,this);
//  else if (c=="staticbox")
//    child=(Child *) new Static(n,"staticbox " + p,pform,this);
  else if (c=="statusbar")
    child=(Child *) new StatusBar(n,p,pform,this);
  else if (c=="table")
    child=(Child *) new Table(n,p,pform,this);
  else if (c=="tab")
    child=(Child *) new Tabs(n,p,pform,this);
  else if (c=="timeedit")
    child=(Child *) new TimeEdit(n,p,pform,this);
  else if (c=="toolbar")
    child=(Child *) new ToolBar(n,p,pform,this);
#ifdef QT_WEBKIT
  else if (c=="webview")
    child=(Child *) new Webview(n,p,pform,this);
#endif
  else {
//    qDebug () << s2q("child not supported " + c);
    sizew=sizeh=0;
    error("child not supported " + c);
    return false;
  }
  if (fontdef) child->setfont(fontdef->font);
  if (child->widget) {
    layout->addWidget(child->widget);
    child->setminwh(sizew,sizeh);
    lasttype=child->type;
    if ((c=="isigraph")||(c=="opengl")) {
      layout->bin->setContentsMargins(0,0,0,0);
      layout->bin->setSpacing(0);
    }
  }
  sizew=sizeh=0;
  pform->addchild(child);
  return true;
}

// ---------------------------------------------------------------------
void Pane::addlayout(Layout *b)
{
  layout=b;
  layouts.append(b);
}

// ---------------------------------------------------------------------
void Pane::bin(string s)
{
  QChar c;
  int i,n;
  Layout *b;
  QString m;
  QStringList p=bsplit(s);

  for (i=0; i<p.size(); i++) {
    m=p.at(i);
    c=m[0];
    n=c_strtoi(q2s(m.mid(1)));
    if (c=='h'||c=='v'||c=='g')
      addlayout(new Layout(c,n,this));
    else if (c=='p' && layout->type!='g')
      layout->addSpacing(n);
    else if (c=='s')
      layout->addStretch(n);
    else if (c=='z' && layouts.size()>1) {
      b=layout;
      n=layouts.last()->stretch;
      layouts.removeLast();
      layout=layouts.last();
      layout->addLayout(b);
    }
  }
}

// ---------------------------------------------------------------------
void Pane::fini()
{
  if (layouts.size()) {
    while (layouts.size()>1)
      bin("z");
    setLayout(layout->bin);
  }
  pform->closepane();
}

// ---------------------------------------------------------------------
void Pane::grid(string c, string s)
{
  if (c=="size") {
    int rmax,cmax;
    QStringList opt=qsplit(s);
    int n=opt.size();
    if (2>n) {
      error("missing grid size row_size or column_size");
      return;
    }
    if (!layout)
      bin("v");
    if ('g'!=layout->type)
      bin("g");
    rmax=c_strtoi(q2s(opt.at(0)));
    cmax=c_strtoi(q2s(opt.at(1)));
    if ((rmax<=0)||(cmax<=0)) {
      error("grid size row_size and column_size must be positive");
      return;
    }
    layout->rmax=rmax;
    layout->cmax=cmax;
    layout->razed=true;
  } else if (c=="cell") {
    int r,c,rs,cs,alignment=0;
    QStringList opt=qsplit(s);
    int n=opt.size();
    if (!(2==n || 3==n || 4==n || 5==n)) {
      error("not grid cell row, column [,row_span, column_span] [,alignment] ");
      return;
    }
    if (!layout)
      bin("v");
    if ('g'!=layout->type)
      bin("g");
    if (layout->razed) {
      error("grid is razed");
      return;
    }
    if (4>n) {
      r=c_strtoi(q2s(opt.at(0)));
      c=c_strtoi(q2s(opt.at(1)));
      rs=cs=1;
      if (3==n) alignment=c_strtoi(q2s(opt.at(2)));
    } else {
      r=c_strtoi(q2s(opt.at(0)));
      c=c_strtoi(q2s(opt.at(1)));
      cs=c_strtoi(q2s(opt.at(2)));
      rs=c_strtoi(q2s(opt.at(3)));
      if (5==n) alignment=c_strtoi(q2s(opt.at(4)));
    }
    if ((r<0)||(c<0)) {
      error("grid cell row and column must be non-negative");
      return;
    }
    if ((rs<=0)||(cs<=0)) {
      error("grid cell row_span and column_span must be positive");
      return;
    }
    if (alignment<0) {
      error("grid cell alignment must be non-negative");
      return;
    }
    layout->r=r;
    layout->c=c;
    layout->rs=rs;
    layout->cs=cs;
    layout->alignment=alignment;
  } else if (c=="colwidth") {
    QStringList opt=qsplit(s);
    int c,w=0;
    int n=opt.size();
    if (2>n) {
      error("grid colwidth must specify column and width");
      return;
    }
    c=c_strtoi(q2s(opt.at(0)));
    w=c_strtoi(q2s(opt.at(1)));
    if ((c<0)||(w<0)) {
      error("grid colwidth column and width must be non-negative");
      return;
    }
    ((QGridLayout *)(layout->bin))->setColumnMinimumWidth(c,w);
  } else if (c=="colstretch") {
    QStringList opt=qsplit(s);
    int c,s=0;
    int n=opt.size();
    if (2>n) {
      error("grid colstretch must specify column and stretch");
      return;
    }
    c=c_strtoi(q2s(opt.at(0)));
    s=c_strtoi(q2s(opt.at(1)));
    if ((c<0)||(s<0)) {
      error("grid colstretch column and stretch must be non-negative");
      return;
    }
    ((QGridLayout *)(layout->bin))->setColumnStretch(c,s);
  } else if (c=="rowheight") {
    QStringList opt=qsplit(s);
    int r,h=0;
    int n=opt.size();
    if (2>n) {
      error("grid row height must specify row and height");
      return;
    }
    r=c_strtoi(q2s(opt.at(0)));
    h=c_strtoi(q2s(opt.at(1)));
    if ((r<0)||(h<0)) {
      error("grid rowheight row and height must be non-negative");
      return;
    }
    ((QGridLayout *)(layout->bin))->setRowMinimumHeight(r,h);
  } else if (c=="rowstretch") {
    QStringList opt=qsplit(s);
    int r,s=0;
    int n=opt.size();
    if (2>n) {
      error("grid row stretch must specify row and height");
      return;
    }
    r=c_strtoi(q2s(opt.at(0)));
    s=c_strtoi(q2s(opt.at(1)));
    if ((r<0)||(s<0)) {
      error("grid rowstretch row and stretch must be non-negative");
      return;
    }
    ((QGridLayout *)(layout->bin))->setRowStretch(r,s);
  } else
    error("bad grid command");
}

// ---------------------------------------------------------------------
bool Pane::groupbox(string c, string s)
{
  QString cmd=s2q(c);
  QString id;

  if (cmd=="groupbox") {
    if (!layout)
      bin("v");
    QStringList opt=qsplit(s);
    if (opt.size())
      id=opt.at(0);
    groupboxw=new QGroupBox(id);
    if (fontdef) groupboxw->setFont(fontdef->font);
    layout->addWidget(groupboxw);
    QVBoxLayout *vb=new QVBoxLayout;
    vb->addWidget(pform->addpane(0));
    groupboxw->setLayout(vb);
    return true;
  }

  if (cmd=="groupboxend") {
    int n=pform->panes.size();
    if (n>1) {
      Pane *p=pform->panes.at(n-2);
      if (p->groupboxw) {
        fini();
        p->groupboxw=0;
        return true;
      }
      error("No groupbox to end");
      return false;
    }
  }
  return false;
}

// ---------------------------------------------------------------------
bool Pane::line(string c, string s)
{
  Q_UNUSED(s);
  QString cmd=s2q(c);
  if (!(cmd=="line" || cmd=="lineh" || cmd=="linev"))
    return false;
  QFrame *f=new QFrame();
  f->setFrameShape((cmd=="linev") ? QFrame::VLine : QFrame::HLine);
  f->setFrameShadow(QFrame::Sunken);
  layout->addWidget(f);
  return true;
}

// ---------------------------------------------------------------------
void Pane::setstretch(Child *cc, string factor)
{
  if (layout->type!='g') ((QBoxLayout *)(layout->bin))->setStretchFactor(cc->widget,atoi(factor.c_str()));
}

// ---------------------------------------------------------------------
bool Pane::split(string c, string s)
{
  if (c=="splith" || c=="splitv") {
    if (!layout)
      bin("v");
    qsplitter=new QSplitter((c=="splith")?Qt::Horizontal : Qt::Vertical);
    qsplitter->addWidget(pform->addpane(1));
    qsplitterp=qs2intlist(s2q(s));
    return true;
  }

  if (!(c=="splitend" || c=="splitsep")) return false;

  fini();
  Pane *sp=pform->pane;

  if (c=="splitend")
    sp->splitend();
  else
    sp->qsplitter->addWidget(pform->addpane(1));
  return true;
}

// ---------------------------------------------------------------------
void Pane::splitend()
{
  if (qsplitterp.size()==4)
    qsplitter->setSizes(qsplitterp.mid(2));
  if (qsplitterp.size()>=2) {
    qsplitter->setStretchFactor(0,qsplitterp[0]);
    qsplitter->setStretchFactor(1,qsplitterp[1]);
  }
  layout->addWidget(qsplitter);
}
<commit_msg>grid: add validation<commit_after>
#include <QGridLayout>
#include <QBoxLayout>
#include <QButtonGroup>
#include <QGroupBox>
#include <QSignalMapper>
#include <QSplitter>

#include "wd.h"
#include "font.h"
#include "cmd.h"
#include "form.h"
#include "pane.h"
#include "child.h"
#include "layout.h"

#include "button.h"
#include "checkbox.h"
#include "combobox.h"
#include "dateedit.h"
#include "dspinbox.h"
#include "dummy.h"
#include "edit.h"
#include "editm.h"
#include "image.h"
#include "isigraph.h"
#include "listbox.h"
#ifdef QT_OPENGL
#include "opengl.h"
#endif
#include "progressbar.h"
#include "radiobutton.h"
#include "slider.h"
#include "spinbox.h"
#include "static.h"
#include "statusbar.h"
#include "table.h"
#include "tabs.h"
#include "timeedit.h"
#include "toolbar.h"
#ifdef QT_WEBKIT
#include "webview.h"
#endif

extern Font *fontdef;

// ---------------------------------------------------------------------
Pane::Pane(int n,Form *f) : QWidget(f)
{
  pform=f;
  buttongroup=0;
  child=0;
  groupboxw=0;
  layout=0;
  sizew=sizeh=0;
  if (n==1) {
    bin("v");
    layout->bin->setContentsMargins(0,0,0,0);
    layout->bin->setSpacing(0);
  }
}

// ---------------------------------------------------------------------
// return if child valid
bool Pane::addchild(string n,string c,string p)
{
  if (!layout)
    bin("v");
  if (c=="button")
    child=(Child *) new Button(n,p,pform,this);
  else if (c=="checkbox")
    child=(Child *) new CheckBox(n,p,pform,this);
  else if (c=="dateedit")
    child=(Child *) new DateEdit(n,p,pform,this);
  else if (c=="dspinbox")
    child=(Child *) new DSpinBox(n,p,pform,this);
  else if (c=="edit")
    child=(Child *) new Edit(n,p,pform,this);
  else if (c=="editm")
    child=(Child *) new Editm(n,p,pform,this);
  else if (c=="combobox")
    child=(Child *) new ComboBox(n,"edit " + p,pform,this);
  else if (c=="combolist")
    child=(Child *) new ComboBox(n,p,pform,this);
  else if (c=="image")
    child=(Child *) new Image(n,p,pform,this);
  else if (c=="isigraph")
    child=(Child *) new Isigraph(n,p,pform,this);
  else if (c=="listbox")
    child=(Child *) new ListBox(n,p,pform,this);
#ifdef QT_OPENGL
  else if (c=="opengl")
    child=(Child *) new Opengl(n,p,pform,this);
#endif
  else if (c=="progressbar")
    child=(Child *) new ProgressBar(n,p,pform,this);
  else if (c=="radiobutton")
    child=(Child *) new RadioButton(n,p,pform,this);
  else if (c=="slider")
    child=(Child *) new Slider(n,p,pform,this);
  else if (c=="spinbox")
    child=(Child *) new SpinBox(n,p,pform,this);
  else if (c=="static")
    child=(Child *) new Static(n,p,pform,this);
//  else if (c=="staticbox")
//    child=(Child *) new Static(n,"staticbox " + p,pform,this);
  else if (c=="statusbar")
    child=(Child *) new StatusBar(n,p,pform,this);
  else if (c=="table")
    child=(Child *) new Table(n,p,pform,this);
  else if (c=="tab")
    child=(Child *) new Tabs(n,p,pform,this);
  else if (c=="timeedit")
    child=(Child *) new TimeEdit(n,p,pform,this);
  else if (c=="toolbar")
    child=(Child *) new ToolBar(n,p,pform,this);
#ifdef QT_WEBKIT
  else if (c=="webview")
    child=(Child *) new Webview(n,p,pform,this);
#endif
  else {
//    qDebug () << s2q("child not supported " + c);
    sizew=sizeh=0;
    error("child not supported " + c);
    return false;
  }
  if (fontdef) child->setfont(fontdef->font);
  if (child->widget) {
    layout->addWidget(child->widget);
    child->setminwh(sizew,sizeh);
    lasttype=child->type;
    if ((c=="isigraph")||(c=="opengl")) {
      layout->bin->setContentsMargins(0,0,0,0);
      layout->bin->setSpacing(0);
    }
  }
  sizew=sizeh=0;
  pform->addchild(child);
  return true;
}

// ---------------------------------------------------------------------
void Pane::addlayout(Layout *b)
{
  layout=b;
  layouts.append(b);
}

// ---------------------------------------------------------------------
void Pane::bin(string s)
{
  QChar c;
  int i,n;
  Layout *b;
  QString m;
  QStringList p=bsplit(s);

  for (i=0; i<p.size(); i++) {
    m=p.at(i);
    c=m[0];
    n=c_strtoi(q2s(m.mid(1)));
    if (c=='h'||c=='v'||c=='g')
      addlayout(new Layout(c,n,this));
    else if (c=='p' && layout->type!='g')
      layout->addSpacing(n);
    else if (c=='s') {
      if ('g'==layout->type) {
        error("grid cannot contain bin s");
        return;
      }
      layout->addStretch(n);
    } else if (c=='z' && layouts.size()>1) {
      b=layout;
      n=layouts.last()->stretch;
      layouts.removeLast();
      layout=layouts.last();
      layout->addLayout(b);
    }
  }
}

// ---------------------------------------------------------------------
void Pane::fini()
{
  if (layouts.size()) {
    while (layouts.size()>1)
      bin("z");
    setLayout(layout->bin);
  }
  pform->closepane();
}

// ---------------------------------------------------------------------
void Pane::grid(string c, string s)
{
  if (c=="size") {
    int rmax,cmax;
    QStringList opt=qsplit(s);
    int n=opt.size();
    if (2>n) {
      error("missing grid size row_size or column_size");
      return;
    }
    if (!layout)
      bin("v");
    if ('g'!=layout->type)
      bin("g");
    rmax=c_strtoi(q2s(opt.at(0)));
    cmax=c_strtoi(q2s(opt.at(1)));
    if ((rmax<=0)||(cmax<=0)) {
      error("grid size row_size and column_size must be positive");
      return;
    }
    layout->rmax=rmax;
    layout->cmax=cmax;
    layout->razed=true;
  } else if (c=="cell") {
    int r,c,rs,cs,alignment=0;
    QStringList opt=qsplit(s);
    int n=opt.size();
    if (!(2==n || 3==n || 4==n || 5==n)) {
      error("not grid cell row, column [,row_span, column_span] [,alignment] ");
      return;
    }
    if (!layout)
      bin("v");
    if ('g'!=layout->type)
      bin("g");
    if (layout->razed) {
      error("grid is razed");
      return;
    }
    if (4>n) {
      r=c_strtoi(q2s(opt.at(0)));
      c=c_strtoi(q2s(opt.at(1)));
      rs=cs=1;
      if (3==n) alignment=c_strtoi(q2s(opt.at(2)));
    } else {
      r=c_strtoi(q2s(opt.at(0)));
      c=c_strtoi(q2s(opt.at(1)));
      cs=c_strtoi(q2s(opt.at(2)));
      rs=c_strtoi(q2s(opt.at(3)));
      if (5==n) alignment=c_strtoi(q2s(opt.at(4)));
    }
    if ((r<0)||(c<0)) {
      error("grid cell row and column must be non-negative");
      return;
    }
    if ((rs<=0)||(cs<=0)) {
      error("grid cell row_span and column_span must be positive");
      return;
    }
    if (alignment<0) {
      error("grid cell alignment must be non-negative");
      return;
    }
    layout->r=r;
    layout->c=c;
    layout->rs=rs;
    layout->cs=cs;
    layout->alignment=alignment;
  } else if (c=="colwidth") {
    QStringList opt=qsplit(s);
    int c,w=0;
    int n=opt.size();
    if (2>n) {
      error("grid colwidth must specify column and width");
      return;
    }
    c=c_strtoi(q2s(opt.at(0)));
    w=c_strtoi(q2s(opt.at(1)));
    if ((c<0)||(w<0)) {
      error("grid colwidth column and width must be non-negative");
      return;
    }
    if (layout->razed && (layout->cmax<=c)) {
      error("grid colwidth invalid column");
      return;
    }
    ((QGridLayout *)(layout->bin))->setColumnMinimumWidth(c,w);
  } else if (c=="colstretch") {
    QStringList opt=qsplit(s);
    int c,s=0;
    int n=opt.size();
    if (2>n) {
      error("grid colstretch must specify column and stretch");
      return;
    }
    c=c_strtoi(q2s(opt.at(0)));
    s=c_strtoi(q2s(opt.at(1)));
    if ((c<0)||(s<0)) {
      error("grid colstretch column and stretch must be non-negative");
      return;
    }
    if (layout->razed && (layout->cmax<=c)) {
      error("grid colstretch invalid column");
      return;
    }
    ((QGridLayout *)(layout->bin))->setColumnStretch(c,s);
  } else if (c=="rowheight") {
    QStringList opt=qsplit(s);
    int r,h=0;
    int n=opt.size();
    if (2>n) {
      error("grid row height must specify row and height");
      return;
    }
    r=c_strtoi(q2s(opt.at(0)));
    h=c_strtoi(q2s(opt.at(1)));
    if ((r<0)||(h<0)) {
      error("grid rowheight row and height must be non-negative");
      return;
    }
    if (layout->razed && (layout->rmax<=r)) {
      error("grid rowheight invalid row");
      return;
    }
    ((QGridLayout *)(layout->bin))->setRowMinimumHeight(r,h);
  } else if (c=="rowstretch") {
    QStringList opt=qsplit(s);
    int r,s=0;
    int n=opt.size();
    if (2>n) {
      error("grid row stretch must specify row and height");
      return;
    }
    r=c_strtoi(q2s(opt.at(0)));
    s=c_strtoi(q2s(opt.at(1)));
    if ((r<0)||(s<0)) {
      error("grid rowstretch row and stretch must be non-negative");
      return;
    }
    if (layout->razed && (layout->rmax<=r)) {
      error("grid rowstretch invalid row");
      return;
    }
    ((QGridLayout *)(layout->bin))->setRowStretch(r,s);
  } else
    error("bad grid command");
}

// ---------------------------------------------------------------------
bool Pane::groupbox(string c, string s)
{
  QString cmd=s2q(c);
  QString id;

  if (cmd=="groupbox") {
    if (!layout)
      bin("v");
    QStringList opt=qsplit(s);
    if (opt.size())
      id=opt.at(0);
    groupboxw=new QGroupBox(id);
    if (fontdef) groupboxw->setFont(fontdef->font);
    layout->addWidget(groupboxw);
    QVBoxLayout *vb=new QVBoxLayout;
    vb->addWidget(pform->addpane(0));
    groupboxw->setLayout(vb);
    return true;
  }

  if (cmd=="groupboxend") {
    int n=pform->panes.size();
    if (n>1) {
      Pane *p=pform->panes.at(n-2);
      if (p->groupboxw) {
        fini();
        p->groupboxw=0;
        return true;
      }
      error("No groupbox to end");
      return false;
    }
  }
  return false;
}

// ---------------------------------------------------------------------
bool Pane::line(string c, string s)
{
  Q_UNUSED(s);
  QString cmd=s2q(c);
  if (!(cmd=="line" || cmd=="lineh" || cmd=="linev"))
    return false;
  QFrame *f=new QFrame();
  f->setFrameShape((cmd=="linev") ? QFrame::VLine : QFrame::HLine);
  f->setFrameShadow(QFrame::Sunken);
  layout->addWidget(f);
  return true;
}

// ---------------------------------------------------------------------
void Pane::setstretch(Child *cc, string factor)
{
  if (layout->type!='g') ((QBoxLayout *)(layout->bin))->setStretchFactor(cc->widget,atoi(factor.c_str()));
}

// ---------------------------------------------------------------------
bool Pane::split(string c, string s)
{
  if (c=="splith" || c=="splitv") {
    if (!layout)
      bin("v");
    qsplitter=new QSplitter((c=="splith")?Qt::Horizontal : Qt::Vertical);
    qsplitter->addWidget(pform->addpane(1));
    qsplitterp=qs2intlist(s2q(s));
    return true;
  }

  if (!(c=="splitend" || c=="splitsep")) return false;

  fini();
  Pane *sp=pform->pane;

  if (c=="splitend")
    sp->splitend();
  else
    sp->qsplitter->addWidget(pform->addpane(1));
  return true;
}

// ---------------------------------------------------------------------
void Pane::splitend()
{
  if (qsplitterp.size()==4)
    qsplitter->setSizes(qsplitterp.mid(2));
  if (qsplitterp.size()>=2) {
    qsplitter->setStretchFactor(0,qsplitterp[0]);
    qsplitter->setStretchFactor(1,qsplitterp[1]);
  }
  layout->addWidget(qsplitter);
}
<|endoftext|>


<commit_before>#pragma once

#include "xml_parser_ctxt.hpp"
#include "pathname.hpp"
#include "libxml2_error_handlers.hpp"
#include "xml_string.hpp"
#include "xml_element.hpp"

#include <string>
#include <iosfwd>
#include <memory>

#include <libxml/tree.h>

struct FreeXmlDoc {
public:
    auto operator( )( xmlDoc *xml ) const -> void { xmlFreeDoc( xml ); }
};

class XmlDoc {
    friend class XmlSchemaParserCtxt;
    friend class XsltDoc;
    friend class XPathCtxt;

public:
    XmlDoc( );
    explicit XmlDoc( std::istream &is );
    XmlDoc( const XmlDoc &doc );
    XmlDoc( XmlDoc &&doc );
    auto operator=( const XmlDoc &rhs ) -> XmlDoc &;
    auto operator=( XmlDoc &&rhs ) -> XmlDoc &;
    explicit operator bool( ) const;
    auto get( ) const -> xmlDoc * { return xmlDoc_.get( ); }
    friend auto operator>>( std::istream &is, XmlDoc &doc ) -> std::istream &;
    auto setRootElement( const std::string &name ) -> XmlElement;
    auto getRootElement( ) const -> XmlElement;
    auto errorHandler( ) const -> const IErrorHandler & { return xmlHandler_; }
    auto toString( ) const -> std::string;
    auto swap( XmlDoc &other ) -> void;

private:
    using XmlDocT = std::shared_ptr<xmlDoc>;
    XmlDocT xmlDoc_;
    XmlErrorHandler xmlHandler_;
};

auto fileToString( const std::string &path ) -> std::string;

inline auto operator<<( std::ostream &os, const XmlDoc &doc ) -> std::ostream & {
    os << doc.toString( );
    return os;
}

namespace std {
    template <>
    void swap( XmlDoc &lhs, XmlDoc &rhs );
}

<commit_msg>added XmlDoc#setRootElement(const XmlElement&) function<commit_after>#pragma once

#include "xml_parser_ctxt.hpp"
#include "pathname.hpp"
#include "libxml2_error_handlers.hpp"
#include "xml_string.hpp"
#include "xml_element.hpp"

#include <string>
#include <iosfwd>
#include <memory>

#include <libxml/tree.h>

struct FreeXmlDoc {
public:
    auto operator( )( xmlDoc *xml ) const -> void { xmlFreeDoc( xml ); }
};

class XmlDoc {
    friend class XmlSchemaParserCtxt;
    friend class XsltDoc;
    friend class XPathCtxt;

public:
    XmlDoc( );
    explicit XmlDoc( std::istream &is );
    XmlDoc( const XmlDoc &doc );
    XmlDoc( XmlDoc &&doc );
    auto operator=( const XmlDoc &rhs ) -> XmlDoc &;
    auto operator=( XmlDoc &&rhs ) -> XmlDoc &;
    explicit operator bool( ) const;
    auto get( ) const -> xmlDoc * { return xmlDoc_.get( ); }
    friend auto operator>>( std::istream &is, XmlDoc &doc ) -> std::istream &;
    auto setRootElement( const std::string &name ) -> void;
    auto setRootElement( const XmlElement &element ) -> void;
    auto getRootElement( ) const -> XmlElement;
    auto errorHandler( ) const -> const IErrorHandler & { return xmlHandler_; }
    auto toString( ) const -> std::string;
    auto swap( XmlDoc &other ) -> void;

private:
    using XmlDocT = std::shared_ptr<xmlDoc>;
    XmlDocT xmlDoc_;
    XmlErrorHandler xmlHandler_;
};

auto fileToString( const std::string &path ) -> std::string;

inline auto operator<<( std::ostream &os, const XmlDoc &doc ) -> std::ostream & {
    os << doc.toString( );
    return os;
}

namespace std {
    template <>
    void swap( XmlDoc &lhs, XmlDoc &rhs );
}

<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////

#include <cppcoro/generator.hpp>
#include <cppcoro/on_scope_exit.hpp>

#include <vector>
#include <string>

#include "doctest/doctest.h"

TEST_SUITE_BEGIN("generator");

using cppcoro::generator;

TEST_CASE("default-constructed generator is empty sequence")
{
	generator<int> ints;
	CHECK(ints.begin() == ints.end());
}

TEST_CASE("generator of arithmetic type returns by copy")
{
	auto f = []() -> generator<float>
	{
		co_yield 1.0f;
		co_yield 2.0f;
	};

	auto gen = f();
	auto iter = gen.begin();
	// TODO: Should this really be required?
	//static_assert(std::is_same<decltype(*iter), float>::value, "operator* should return float by value");
	CHECK(*iter == 1.0f);
	++iter;
	CHECK(*iter == 2.0f);
	++iter;
	CHECK(iter == gen.end());
}

TEST_CASE("generator of reference returns by reference")
{
	auto f = [](float& value) -> generator<float&>
	{
		co_yield value;
	};

	float value = 1.0f;
	for (auto& x : f(value))
	{
		CHECK(&x == &value);
		x += 1.0f;
	}

	CHECK(value == 2.0f);
}

TEST_CASE("generator doesn't start until its called")
{
	bool reachedA = false;
	bool reachedB = false;
	bool reachedC = false;
	auto f = [&]() -> generator<int>
	{
		reachedA = true;
		co_yield 1;
		reachedB = true;
		co_yield 2;
		reachedC = true;
	};

	auto gen = f();
	CHECK(!reachedA);
	auto iter = gen.begin();
	CHECK(reachedA);
	CHECK(!reachedB);
	CHECK(*iter == 1);
	++iter;
	CHECK(reachedB);
	CHECK(!reachedC);
	CHECK(*iter == 2);
	++iter;
	CHECK(reachedC);
	CHECK(iter == gen.end());
}

TEST_CASE("destroying generator before completion destructs objects on stack")
{
	bool destructed = false;
	bool completed = false;
	auto f = [&]() -> generator<int>
	{
		auto onExit = cppcoro::on_scope_exit([&]
		{
			destructed = true;
		});

		co_yield 1;
		co_yield 2;
		completed = true;
	};

	{
		auto g = f();
		auto it = g.begin();
		auto itEnd = g.end();
		CHECK(it != itEnd);
		CHECK(*it == 1u);
		CHECK(!destructed);
	}

	CHECK(!completed);
	CHECK(destructed);
}

TEST_CASE("generator throwing before yielding first element rethrows out of begin()")
{
	class X {};

	auto g = []() -> cppcoro::generator<int>
	{
		throw X{};
		co_return;
	}();

	try
	{
		g.begin();
		FAIL("should have thrown");
	}
	catch (const X&)
	{
	}
}

TEST_CASE("generator throwing after first element rethrows out of operator++")
{
	class X {};

	auto g = []() -> cppcoro::generator<int>
	{
		co_yield 1;
		throw X{};
	}();

	auto iter = g.begin();
	REQUIRE(iter != g.end());
	try
	{
		++iter;
		FAIL("should have thrown");
	}
	catch (const X&)
	{
	}
}

template<typename FIRST, typename SECOND>
auto concat(FIRST&& first, SECOND&& second)
{
	using value_type = std::remove_reference_t<decltype(*first.begin())>;
	return [](FIRST first, SECOND second) -> cppcoro::generator<value_type>
	{
		for (auto&& x : first) co_yield x;
		for (auto&& y : second) co_yield y;
	}(std::forward<FIRST>(first), std::forward<SECOND>(second));
}

TEST_CASE("safe capture of r-value reference args")
{
	using namespace std::string_literals;

	// Check that we can capture l-values by reference and that temporary
	// values are moved into the coroutine frame.
	std::string byRef = "bar";
	auto g = concat("foo"s, concat(byRef, std::vector<char>{ 'b', 'a', 'z' }));

	byRef = "buzz";

	std::string s;
	for (char c : g)
	{
		s += c;
	}

	CHECK(s == "foobuzzbaz");
}

TEST_SUITE_END();
<commit_msg>fixed missing include<commit_after>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////

#include <cppcoro/generator.hpp>
#include <cppcoro/on_scope_exit.hpp>

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

#include "doctest/doctest.h"

TEST_SUITE_BEGIN("generator");

using cppcoro::generator;

TEST_CASE("default-constructed generator is empty sequence")
{
	generator<int> ints;
	CHECK(ints.begin() == ints.end());
}

TEST_CASE("generator of arithmetic type returns by copy")
{
	auto f = []() -> generator<float>
	{
		co_yield 1.0f;
		co_yield 2.0f;
	};

	auto gen = f();
	auto iter = gen.begin();
	// TODO: Should this really be required?
	//static_assert(std::is_same<decltype(*iter), float>::value, "operator* should return float by value");
	CHECK(*iter == 1.0f);
	++iter;
	CHECK(*iter == 2.0f);
	++iter;
	CHECK(iter == gen.end());
}

TEST_CASE("generator of reference returns by reference")
{
	auto f = [](float& value) -> generator<float&>
	{
		co_yield value;
	};

	float value = 1.0f;
	for (auto& x : f(value))
	{
		CHECK(&x == &value);
		x += 1.0f;
	}

	CHECK(value == 2.0f);
}

TEST_CASE("generator doesn't start until its called")
{
	bool reachedA = false;
	bool reachedB = false;
	bool reachedC = false;
	auto f = [&]() -> generator<int>
	{
		reachedA = true;
		co_yield 1;
		reachedB = true;
		co_yield 2;
		reachedC = true;
	};

	auto gen = f();
	CHECK(!reachedA);
	auto iter = gen.begin();
	CHECK(reachedA);
	CHECK(!reachedB);
	CHECK(*iter == 1);
	++iter;
	CHECK(reachedB);
	CHECK(!reachedC);
	CHECK(*iter == 2);
	++iter;
	CHECK(reachedC);
	CHECK(iter == gen.end());
}

TEST_CASE("destroying generator before completion destructs objects on stack")
{
	bool destructed = false;
	bool completed = false;
	auto f = [&]() -> generator<int>
	{
		auto onExit = cppcoro::on_scope_exit([&]
		{
			destructed = true;
		});

		co_yield 1;
		co_yield 2;
		completed = true;
	};

	{
		auto g = f();
		auto it = g.begin();
		auto itEnd = g.end();
		CHECK(it != itEnd);
		CHECK(*it == 1u);
		CHECK(!destructed);
	}

	CHECK(!completed);
	CHECK(destructed);
}

TEST_CASE("generator throwing before yielding first element rethrows out of begin()")
{
	class X {};

	auto g = []() -> cppcoro::generator<int>
	{
		throw X{};
		co_return;
	}();

	try
	{
		g.begin();
		FAIL("should have thrown");
	}
	catch (const X&)
	{
	}
}

TEST_CASE("generator throwing after first element rethrows out of operator++")
{
	class X {};

	auto g = []() -> cppcoro::generator<int>
	{
		co_yield 1;
		throw X{};
	}();

	auto iter = g.begin();
	REQUIRE(iter != g.end());
	try
	{
		++iter;
		FAIL("should have thrown");
	}
	catch (const X&)
	{
	}
}

template<typename FIRST, typename SECOND>
auto concat(FIRST&& first, SECOND&& second)
{
	using value_type = std::remove_reference_t<decltype(*first.begin())>;
	return [](FIRST first, SECOND second) -> cppcoro::generator<value_type>
	{
		for (auto&& x : first) co_yield x;
		for (auto&& y : second) co_yield y;
	}(std::forward<FIRST>(first), std::forward<SECOND>(second));
}

TEST_CASE("safe capture of r-value reference args")
{
	using namespace std::string_literals;

	// Check that we can capture l-values by reference and that temporary
	// values are moved into the coroutine frame.
	std::string byRef = "bar";
	auto g = concat("foo"s, concat(byRef, std::vector<char>{ 'b', 'a', 'z' }));

	byRef = "buzz";

	std::string s;
	for (char c : g)
	{
		s += c;
	}

	CHECK(s == "foobuzzbaz");
}

TEST_SUITE_END();
<|endoftext|>


<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
// Copyright (C) 2014 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

static bool g_called;
#define EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN { g_called = true; }

#include "main.h"

template<typename MatrixType> void linearStructure(const MatrixType& m)
{
  using std::abs;
  /* this test covers the following files:
     CwiseUnaryOp.h, CwiseBinaryOp.h, SelfCwiseBinaryOp.h
  */
  typedef typename MatrixType::Index Index;
  typedef typename MatrixType::Scalar Scalar;

  Index rows = m.rows();
  Index cols = m.cols();

  // this test relies a lot on Random.h, and there's not much more that we can do
  // to test it, hence I consider that we will have tested Random.h
  MatrixType m1 = MatrixType::Random(rows, cols),
             m2 = MatrixType::Random(rows, cols),
             m3(rows, cols);

  Scalar s1 = internal::random<Scalar>();
  while (abs(s1)<1e-3) s1 = internal::random<Scalar>();

  Index r = internal::random<Index>(0, rows-1),
        c = internal::random<Index>(0, cols-1);

  VERIFY_IS_APPROX(-(-m1),                  m1);
  VERIFY_IS_APPROX(m1+m1,                   2*m1);
  VERIFY_IS_APPROX(m1+m2-m1,                m2);
  VERIFY_IS_APPROX(-m2+m1+m2,               m1);
  VERIFY_IS_APPROX(m1*s1,                   s1*m1);
  VERIFY_IS_APPROX((m1+m2)*s1,              s1*m1+s1*m2);
  VERIFY_IS_APPROX((-m1+m2)*s1,             -s1*m1+s1*m2);
  m3 = m2; m3 += m1;
  VERIFY_IS_APPROX(m3,                      m1+m2);
  m3 = m2; m3 -= m1;
  VERIFY_IS_APPROX(m3,                      m2-m1);
  m3 = m2; m3 *= s1;
  VERIFY_IS_APPROX(m3,                      s1*m2);
  if(!NumTraits<Scalar>::IsInteger)
  {
    m3 = m2; m3 /= s1;
    VERIFY_IS_APPROX(m3,                    m2/s1);
  }

  // again, test operator() to check const-qualification
  VERIFY_IS_APPROX((-m1)(r,c), -(m1(r,c)));
  VERIFY_IS_APPROX((m1-m2)(r,c), (m1(r,c))-(m2(r,c)));
  VERIFY_IS_APPROX((m1+m2)(r,c), (m1(r,c))+(m2(r,c)));
  VERIFY_IS_APPROX((s1*m1)(r,c), s1*(m1(r,c)));
  VERIFY_IS_APPROX((m1*s1)(r,c), (m1(r,c))*s1);
  if(!NumTraits<Scalar>::IsInteger)
    VERIFY_IS_APPROX((m1/s1)(r,c), (m1(r,c))/s1);

  // use .block to disable vectorization and compare to the vectorized version
  VERIFY_IS_APPROX(m1+m1.block(0,0,rows,cols), m1+m1);
  VERIFY_IS_APPROX(m1.cwiseProduct(m1.block(0,0,rows,cols)), m1.cwiseProduct(m1));
  VERIFY_IS_APPROX(m1 - m1.block(0,0,rows,cols), m1 - m1);
  VERIFY_IS_APPROX(m1.block(0,0,rows,cols) * s1, m1 * s1);
}

// Make sure that complex * real and real * complex are properly optimized
template<typename MatrixType> void real_complex(DenseIndex rows = MatrixType::RowsAtCompileTime, DenseIndex cols = MatrixType::ColsAtCompileTime)
{
  typedef typename MatrixType::Scalar Scalar;
  typedef typename MatrixType::RealScalar RealScalar;

  RealScalar s = internal::random<RealScalar>();
  MatrixType m1 = MatrixType::Random(rows, cols);

  g_called = false;
  VERIFY_IS_APPROX(s*m1, Scalar(s)*m1);
  VERIFY(g_called && "real * matrix<complex> not properly optimized");

  g_called = false;
  VERIFY_IS_APPROX(m1*s, m1*Scalar(s));
  VERIFY(g_called && "matrix<complex> * real not properly optimized");
}

void test_linearstructure()
{
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( linearStructure(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_2( linearStructure(Matrix2f()) );
    CALL_SUBTEST_3( linearStructure(Vector3d()) );
    CALL_SUBTEST_4( linearStructure(Matrix4d()) );
    CALL_SUBTEST_5( linearStructure(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) );
    CALL_SUBTEST_6( linearStructure(MatrixXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );
    CALL_SUBTEST_7( linearStructure(MatrixXi (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );
    CALL_SUBTEST_8( linearStructure(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) );
    CALL_SUBTEST_9( linearStructure(ArrayXXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );

    CALL_SUBTEST_10( real_complex<Matrix4cd>() );
    CALL_SUBTEST_10( real_complex<MatrixXcf>(10,10) );
  }

#ifdef EIGEN_TEST_PART_4
  {
    // make sure that /=scalar and /scalar do not overflow
    // rational: 1.0/4.94e-320 overflow, but m/4.94e-320 should not
    Matrix4d m2, m3;
    m3 = m2 =  Matrix4d::Random()*1e-20;
    m2 = m2 / 4.9e-320;
    VERIFY_IS_APPROX(m2.cwiseQuotient(m2), Matrix4d::Ones());
    m3 /= 4.9e-320;
    VERIFY_IS_APPROX(m3.cwiseQuotient(m3), Matrix4d::Ones());

  }
#endif
}
<commit_msg>Avoid `unneeded-internal-declaration' warning<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
// Copyright (C) 2014 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

static bool g_called;
#define EIGEN_SPECIAL_SCALAR_MULTIPLE_PLUGIN { g_called = true; }

#include "main.h"

template<typename MatrixType> void linearStructure(const MatrixType& m)
{
  using std::abs;
  /* this test covers the following files:
     CwiseUnaryOp.h, CwiseBinaryOp.h, SelfCwiseBinaryOp.h
  */
  typedef typename MatrixType::Index Index;
  typedef typename MatrixType::Scalar Scalar;

  Index rows = m.rows();
  Index cols = m.cols();

  // this test relies a lot on Random.h, and there's not much more that we can do
  // to test it, hence I consider that we will have tested Random.h
  MatrixType m1 = MatrixType::Random(rows, cols),
             m2 = MatrixType::Random(rows, cols),
             m3(rows, cols);

  Scalar s1 = internal::random<Scalar>();
  while (abs(s1)<1e-3) s1 = internal::random<Scalar>();

  Index r = internal::random<Index>(0, rows-1),
        c = internal::random<Index>(0, cols-1);

  VERIFY_IS_APPROX(-(-m1),                  m1);
  VERIFY_IS_APPROX(m1+m1,                   2*m1);
  VERIFY_IS_APPROX(m1+m2-m1,                m2);
  VERIFY_IS_APPROX(-m2+m1+m2,               m1);
  VERIFY_IS_APPROX(m1*s1,                   s1*m1);
  VERIFY_IS_APPROX((m1+m2)*s1,              s1*m1+s1*m2);
  VERIFY_IS_APPROX((-m1+m2)*s1,             -s1*m1+s1*m2);
  m3 = m2; m3 += m1;
  VERIFY_IS_APPROX(m3,                      m1+m2);
  m3 = m2; m3 -= m1;
  VERIFY_IS_APPROX(m3,                      m2-m1);
  m3 = m2; m3 *= s1;
  VERIFY_IS_APPROX(m3,                      s1*m2);
  if(!NumTraits<Scalar>::IsInteger)
  {
    m3 = m2; m3 /= s1;
    VERIFY_IS_APPROX(m3,                    m2/s1);
  }

  // again, test operator() to check const-qualification
  VERIFY_IS_APPROX((-m1)(r,c), -(m1(r,c)));
  VERIFY_IS_APPROX((m1-m2)(r,c), (m1(r,c))-(m2(r,c)));
  VERIFY_IS_APPROX((m1+m2)(r,c), (m1(r,c))+(m2(r,c)));
  VERIFY_IS_APPROX((s1*m1)(r,c), s1*(m1(r,c)));
  VERIFY_IS_APPROX((m1*s1)(r,c), (m1(r,c))*s1);
  if(!NumTraits<Scalar>::IsInteger)
    VERIFY_IS_APPROX((m1/s1)(r,c), (m1(r,c))/s1);

  // use .block to disable vectorization and compare to the vectorized version
  VERIFY_IS_APPROX(m1+m1.block(0,0,rows,cols), m1+m1);
  VERIFY_IS_APPROX(m1.cwiseProduct(m1.block(0,0,rows,cols)), m1.cwiseProduct(m1));
  VERIFY_IS_APPROX(m1 - m1.block(0,0,rows,cols), m1 - m1);
  VERIFY_IS_APPROX(m1.block(0,0,rows,cols) * s1, m1 * s1);
}

// Make sure that complex * real and real * complex are properly optimized
template<typename MatrixType> void real_complex(DenseIndex rows = MatrixType::RowsAtCompileTime, DenseIndex cols = MatrixType::ColsAtCompileTime)
{
  typedef typename MatrixType::Scalar Scalar;
  typedef typename MatrixType::RealScalar RealScalar;

  RealScalar s = internal::random<RealScalar>();
  MatrixType m1 = MatrixType::Random(rows, cols);

  g_called = false;
  VERIFY_IS_APPROX(s*m1, Scalar(s)*m1);
  VERIFY(g_called && "real * matrix<complex> not properly optimized");

  g_called = false;
  VERIFY_IS_APPROX(m1*s, m1*Scalar(s));
  VERIFY(g_called && "matrix<complex> * real not properly optimized");
}

void test_linearstructure()
{
  g_called = true;
  VERIFY(g_called); // avoid `unneeded-internal-declaration` warning.
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( linearStructure(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_2( linearStructure(Matrix2f()) );
    CALL_SUBTEST_3( linearStructure(Vector3d()) );
    CALL_SUBTEST_4( linearStructure(Matrix4d()) );
    CALL_SUBTEST_5( linearStructure(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) );
    CALL_SUBTEST_6( linearStructure(MatrixXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );
    CALL_SUBTEST_7( linearStructure(MatrixXi (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );
    CALL_SUBTEST_8( linearStructure(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) );
    CALL_SUBTEST_9( linearStructure(ArrayXXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) );

    CALL_SUBTEST_10( real_complex<Matrix4cd>() );
    CALL_SUBTEST_10( real_complex<MatrixXcf>(10,10) );
  }

#ifdef EIGEN_TEST_PART_4
  {
    // make sure that /=scalar and /scalar do not overflow
    // rational: 1.0/4.94e-320 overflow, but m/4.94e-320 should not
    Matrix4d m2, m3;
    m3 = m2 =  Matrix4d::Random()*1e-20;
    m2 = m2 / 4.9e-320;
    VERIFY_IS_APPROX(m2.cwiseQuotient(m2), Matrix4d::Ones());
    m3 /= 4.9e-320;
    VERIFY_IS_APPROX(m3.cwiseQuotient(m3), Matrix4d::Ones());

  }
#endif
}
<|endoftext|>