hackedteam/vector-dropper

View on GitHub
RCSStreamingMelter/lib/DataState.h

Summary

Maintainability
Test Coverage
/*
 * DataState.h
 *
 *  Created on: Apr 27, 2010
 *      Author: daniele
 */

#ifndef DATASTATE_H_
#define DATASTATE_H_

#include <boost/statechart/result.hpp>
#include <boost/statechart/deep_history.hpp>
#include <boost/statechart/custom_reaction.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/transition.hpp>
#include <boost/statechart/in_state_reaction.hpp>
namespace sc = boost::statechart;

#include "Common.h"
#include "Events.h"
#include "StreamingMelter.h"

enum StateResult {
        NEED_MORE_DATA,
        PARSED,
        PROCESSED,
    };

template< class Derived, class Outer >
struct DataState : sc::simple_state< Derived, Outer >
{
public:
    DataState()
        : done_(false), initialized_(false), parsed_(false)
    {
    }

    void feed ( char *data, std::size_t size )
    {
        this.template context<StreamingMelter>().feed(data, size);
        this->post_event( EvNewData() );
    }

    bool isTriggering()
    {
        if ( currentOffset() == triggeringOffset() && isDataAvailable() )
            return true;
        return false;
    }

    bool isDataAvailable()
    {
        return isDataAvailable( neededBytes() );
    }

    bool isDataAvailable(std::size_t bytes)
    {
        //DBGTRACE("isDataAvailable - available offset: ", availableOffset(), NOTIFY);
        //DBGTRACE("isDataAvailable - current offset  : ", currentOffset(), NOTIFY);
        //DBGTRACE("isDataAvailable - needed bytes    : ", bytes, NOTIFY);
        if ( availableOffset() - currentOffset() >= bytes )
            return true;
        return false;
    }

    sc::result canSwitchToNext()
    {
        if ( availableOffset() < offsetToNext() ) {
            std::size_t bytes = availableOffset() - currentOffset();
            this->template context<StreamingMelter>().complete( bytes );
            return this->discard_event();
        }

        std::size_t bytes = offsetToNext() - currentOffset();
        this->template context<StreamingMelter>().complete( bytes );
        this->post_event( EvNewData() );
        return transitToNext();
    }

    char* getData()
    {
        char *data = this->template context<StreamingMelter>().buffer()->data();
        if (!data)
                throw parsing_error("Invalid data pointer.");
        return data;
    }

    virtual void init() = 0;
    virtual StateResult parse() = 0;
    virtual StateResult process() { return PROCESSED; };
    virtual sc::result transitToNext() = 0;

    void preamble()
    {
    }

    sc::result react( const EvNewData & ev )
    {
        (void) ev;

        if ( ! initialized() )
        {
            init();
            initialized() = true;
        }

        //DBGTRACE_BUFFER(DEVDEBUG);

        if ( done() == false )
        {
            // check if we have enough data
            if ( ! isTriggering() ) {
                // DBGTRACE("NOT TRIGGERING", "", DEVDEBUG);
                return this->discard_event();
            }

            // DBGTRACE("TRIGGERING", "", DEVDEBUG);
            if (!parsed_) {
                if (parse() != PARSED)
                    return this->discard_event();
                parsed_ = true;
            }

            if (process() != PROCESSED)
                throw parsing_error("CRITICAL ERROR!!!");

            done() = true;
            // DBGTRACE("offset to next           : ", offsetToNext(), DEVDEBUG);
        }

        // DBGTRACE("running to next state in: ", offsetToNext() - currentOffset(), DEVDEBUG);
        return canSwitchToNext();
    }

    boost::shared_ptr<Chunk> output()
    {
        DEBUG_MSG(D_EXCESSIVE, "new data to offset: %08x", this.template context<StreamingMelter>().maxOffset());
        return this.template context<StreamingMelter>().output();
    }

    std::size_t currentOffset()
    {
        return this->template context<StreamingMelter>().currentOffset();
    }

    std::size_t availableOffset()
    {
        return this->template context<StreamingMelter>().maxOffset();
    }

    bool done() const { return done_; }
    bool & done() { return done_; }

    std::size_t neededBytes() const { return neededBytes_; }
    std::size_t& neededBytes() { return neededBytes_; }

    std::size_t triggeringOffset() const { return triggeringOffset_; }
    std::size_t& triggeringOffset() { return triggeringOffset_; }

    std::size_t offsetToNext() const { return offsetToNext_; }
    std::size_t& offsetToNext() { return offsetToNext_; }

    bool initialized() const { return initialized_; }
    bool& initialized() { return initialized_; }

    typedef mpl::list<
                sc::custom_reaction<EvNewData>,
                sc::transition<EvParsingFailed, Defective>
            > reactions;

protected:
    std::size_t neededBytes_;
    std::size_t triggeringOffset_;
    std::size_t offsetToNext_;
    bool done_;
    bool initialized_;
    bool parsed_;
};

#endif /* DATASTATE_H_ */