<commit_before>#include "rabbit_mq_face.hpp"
#include "parcel_generated.h"
#include "fatal_error.hpp"
#include <amqp_tcp_socket.h>
#include <chucho/log.hpp>
#include <sstream>

namespace
{

constexpr const int YELLA_CHANNEL = 1;

void respond(const amqp_rpc_reply_t& rep)
{
    if (rep.reply_type == AMQP_RESPONSE_NORMAL)
        return;
    std::ostringstream stream;
    stream << "The RabbitMQ broker reports an error: ";
    if (rep.reply_type == AMQP_RESPONSE_NONE)
    {
        stream << "Missing reply type";
    }
    else if (rep.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION)
    {
        stream << amqp_error_string2(rep.library_error);
    }
    else if (rep.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION)
    {
        if (rep.reply.id == AMQP_CHANNEL_CLOSE_METHOD)
        {
            auto ch = reinterpret_cast<amqp_channel_close_t*>(rep.reply.decoded);
            stream << "Channel: (" << ch->reply_code << ") " <<
                   std::string(reinterpret_cast<char*>(ch->reply_text.bytes), ch->reply_text.len);
        }
        else if (rep.reply.id == AMQP_CONNECTION_CLOSE_METHOD)
        {
            auto cn = reinterpret_cast<amqp_connection_close_t*>(rep.reply.decoded);
            stream << "Connection: (" << cn->reply_code << ") " <<
                   std::string(reinterpret_cast<char*>(cn->reply_text.bytes), cn->reply_text.len);
        }
        else
        {
            stream << "Unknown method: " << rep.reply.id;
        }
    }
    throw yella::router::fatal_error(stream.str());
}

}

namespace yella
{

namespace router
{

rabbit_mq_face::rabbit_mq_face(const configuration& cnf)
    : mq_face(cnf),
      should_stop_(false)
{
}

rabbit_mq_face::~rabbit_mq_face()
{
    should_stop_ = true;
    receiver_.join();
}

amqp_connection_state_t rabbit_mq_face::create_connection()
{
    amqp_connection_state_t result;
    try
    {
        amqp_connection_info info;
        std::vector<char> url(config_.mq_broker().c_str(), config_.mq_broker().c_str() + config_.mq_broker().length() + 1);
        int rc = amqp_parse_url(&url[0], &info);
        if (rc != AMQP_STATUS_OK)
            throw std::invalid_argument("Invalid AMQP URL: " + config_.mq_broker());
        if (info.ssl)
            throw std::invalid_argument("SSL is not supported: " + config_.mq_broker());
        result = amqp_new_connection();
        amqp_socket_t* sock = amqp_tcp_socket_new(result);
        if (sock == nullptr)
            throw std::runtime_error("Unable to create TCP socket");
        rc = amqp_socket_open(sock, info.host, info.port);
        if (rc != AMQP_STATUS_OK)
            throw std::runtime_error(std::string("Unable to open socket: ") + amqp_error_string2(rc));
        amqp_rpc_reply_t rep = amqp_login(result,
                                          info.vhost,
                                          0,
                                          AMQP_DEFAULT_FRAME_SIZE,
                                          0,
                                          AMQP_SASL_METHOD_PLAIN,
                                          info.user,
                                          info.password);
        respond(rep);
        amqp_channel_open(result, YELLA_CHANNEL);
        rep = amqp_get_rpc_reply(result);
        respond(rep);
        CHUCHO_INFO_L("Connected to RabbitMQ server");
    }
    catch (const std::exception& e)
    {
        amqp_destroy_connection(result);
        throw;
    }
    return result;
}

void rabbit_mq_face::receiver_main()
{
    CHUCHO_INFO_L_STR("Message queue receiver is starting");
    amqp_connection_state_t cxn = nullptr;
    try
    {
        cxn = create_connection();
        for (const auto& q : config_.consumption_queues())
        {
            amqp_bytes_t qbytes;
            qbytes.bytes = const_cast<char*>(q.data());
            qbytes.len = q.length();
            amqp_basic_consume(cxn,
                               YELLA_CHANNEL,
                               qbytes,
                               amqp_empty_bytes,
                               0, // no local
                               1, // no ack
                               0, // exclusive
                               amqp_empty_table);
            auto rep = amqp_get_rpc_reply(cxn);
            respond(rep);
        }
        amqp_envelope_t env;
        struct timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
        while (!should_stop_)
        {
            amqp_maybe_release_buffers(cxn);
            auto rep = amqp_consume_message(cxn, &env, &timeout, 0);
            if (rep.reply_type != AMQP_RESPONSE_NONE)
            {
                respond(rep);
                other_face_->send(reinterpret_cast<uint8_t*>(env.message.body.bytes), env.message.body.len);
                amqp_destroy_envelope(&env);
            }
        }
    }
    catch (const std::exception& e)
    {
        CHUCHO_FATAL_L("Error occurred with broker: " << e.what());
        callback_of_death_();
    }
    if (cxn != nullptr)
    {
        amqp_channel_close(cxn, YELLA_CHANNEL, AMQP_REPLY_SUCCESS);
        amqp_connection_close(cxn, AMQP_REPLY_SUCCESS);
        amqp_destroy_connection(cxn);
    }
    CHUCHO_INFO_L_STR("Message queue receiver is ending");
}

void rabbit_mq_face::run(face* other_face,
                         std::function<void()> callback_of_death)
{
    mq_face::run(other_face, callback_of_death);
    receiver_ = std::thread(&rabbit_mq_face::receiver_main, this);
}

void rabbit_mq_face::send(const std::uint8_t* const msg, std::size_t len)
{
    auto found = senders_.find(std::this_thread::get_id());
    if (found == senders_.end())
    {
        found = senders_.emplace(std::piecewise_construct,
                                 std::forward_as_tuple(std::this_thread::get_id()),
                                 std::forward_as_tuple(*this)).first;
    }
    found->second.send(msg, len);
}

rabbit_mq_face::sender::sender(rabbit_mq_face& rmf)
    : config_(rmf.config_),
      cxn_(rmf.create_connection())
{
}

rabbit_mq_face::sender::~sender()
{
    amqp_channel_close(cxn_, YELLA_CHANNEL, AMQP_REPLY_SUCCESS);
    amqp_connection_close(cxn_, AMQP_REPLY_SUCCESS);
    amqp_destroy_connection(cxn_);
}

void rabbit_mq_face::sender::send(const std::uint8_t* const msg, std::size_t len)
{
    auto parcel = yella::fb::Getparcel(msg);
    auto type = parcel->type();
    if (type == nullptr)
    {
        throw std::runtime_error("No type included in the parcel");
    }
    else
    {
        auto tstr = type->str();
        auto xbytes = amqp_cstring_bytes(tstr.c_str());
        auto found = exchanges_.find(tstr);
        if (found == exchanges_.end())
        {
            amqp_exchange_declare(cxn_,
                                  YELLA_CHANNEL,
                                  xbytes,
                                  amqp_cstring_bytes("amq.fanout"),
                                  0, // passive
                                  1, // durable
                                  0, // auto-delete
                                  0, // internal
                                  amqp_empty_table);
            amqp_rpc_reply_t rep = amqp_get_rpc_reply(cxn_);
            respond(rep);
            if (config_.bind_exchanges())
            {
                amqp_queue_declare(cxn_,
                                   YELLA_CHANNEL,
                                   xbytes,
                                   0, // passive
                                   1, // durable
                                   0, // exclusive
                                   0, // auto-delete
                                   amqp_empty_table);
                rep = amqp_get_rpc_reply(cxn_);
                respond(rep);
                amqp_queue_bind(cxn_,
                                YELLA_CHANNEL,
                                xbytes,
                                xbytes,
                                amqp_empty_bytes,
                                amqp_empty_table);
                rep = amqp_get_rpc_reply(cxn_);
                respond(rep);
            }
            exchanges_.insert(tstr);
        }
        amqp_bytes_t mbytes;
        mbytes.bytes = const_cast<uint8_t*>(msg);
        mbytes.len = len;
        int rc = amqp_basic_publish(cxn_,
                                    YELLA_CHANNEL,
                                    xbytes,
                                    amqp_empty_bytes,
                                    0, // mandatory
                                    0, // immediate
                                    nullptr, // properties
                                    mbytes);
        if (rc != AMQP_STATUS_OK)
            throw std::runtime_error(std::string("Error publishing to RabbitMQ: ") + amqp_error_string2(rc));
    }
}

}

}<commit_msg>Everyone is listening and shutting down<commit_after>#include "rabbit_mq_face.hpp"
#include "parcel_generated.h"
#include "fatal_error.hpp"
#include <amqp_tcp_socket.h>
#include <chucho/log.hpp>
#include <sstream>

namespace
{

constexpr const int YELLA_CHANNEL = 1;

void respond(const amqp_rpc_reply_t& rep)
{
    if (rep.reply_type == AMQP_RESPONSE_NORMAL)
        return;
    std::ostringstream stream;
    stream << "The RabbitMQ broker reports an error: ";
    if (rep.reply_type == AMQP_RESPONSE_NONE)
    {
        stream << "Missing reply type";
    }
    else if (rep.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION)
    {
        stream << amqp_error_string2(rep.library_error);
    }
    else if (rep.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION)
    {
        if (rep.reply.id == AMQP_CHANNEL_CLOSE_METHOD)
        {
            auto ch = reinterpret_cast<amqp_channel_close_t*>(rep.reply.decoded);
            stream << "Channel: (" << ch->reply_code << ") " <<
                   std::string(reinterpret_cast<char*>(ch->reply_text.bytes), ch->reply_text.len);
        }
        else if (rep.reply.id == AMQP_CONNECTION_CLOSE_METHOD)
        {
            auto cn = reinterpret_cast<amqp_connection_close_t*>(rep.reply.decoded);
            stream << "Connection: (" << cn->reply_code << ") " <<
                   std::string(reinterpret_cast<char*>(cn->reply_text.bytes), cn->reply_text.len);
        }
        else
        {
            stream << "Unknown method: " << rep.reply.id;
        }
    }
    throw yella::router::fatal_error(stream.str());
}

}

namespace yella
{

namespace router
{

rabbit_mq_face::rabbit_mq_face(const configuration& cnf)
    : mq_face(cnf),
      should_stop_(false)
{
}

rabbit_mq_face::~rabbit_mq_face()
{
    should_stop_ = true;
    receiver_.join();
}

amqp_connection_state_t rabbit_mq_face::create_connection()
{
    amqp_connection_state_t result;
    try
    {
        amqp_connection_info info;
        std::vector<char> url(config_.mq_broker().c_str(), config_.mq_broker().c_str() + config_.mq_broker().length() + 1);
        int rc = amqp_parse_url(&url[0], &info);
        if (rc != AMQP_STATUS_OK)
            throw std::invalid_argument("Invalid AMQP URL: " + config_.mq_broker());
        if (info.ssl)
            throw std::invalid_argument("SSL is not supported: " + config_.mq_broker());
        result = amqp_new_connection();
        amqp_socket_t* sock = amqp_tcp_socket_new(result);
        if (sock == nullptr)
            throw std::runtime_error("Unable to create TCP socket");
        rc = amqp_socket_open(sock, info.host, info.port);
        if (rc != AMQP_STATUS_OK)
            throw std::runtime_error(std::string("Unable to open socket: ") + amqp_error_string2(rc));
        amqp_rpc_reply_t rep = amqp_login(result,
                                          info.vhost,
                                          0,
                                          AMQP_DEFAULT_FRAME_SIZE,
                                          0,
                                          AMQP_SASL_METHOD_PLAIN,
                                          info.user,
                                          info.password);
        respond(rep);
        amqp_channel_open(result, YELLA_CHANNEL);
        rep = amqp_get_rpc_reply(result);
        respond(rep);
        CHUCHO_INFO_L("Connected to RabbitMQ server");
    }
    catch (const std::exception& e)
    {
        amqp_destroy_connection(result);
        throw;
    }
    return result;
}

void rabbit_mq_face::receiver_main()
{
    CHUCHO_INFO_L_STR("Message queue receiver is starting");
    amqp_connection_state_t cxn = nullptr;
    try
    {
        cxn = create_connection();
        for (const auto& q : config_.consumption_queues())
        {
            amqp_bytes_t qbytes;
            qbytes.bytes = const_cast<char*>(q.data());
            qbytes.len = q.length();
            amqp_basic_consume(cxn,
                               YELLA_CHANNEL,
                               qbytes,
                               amqp_empty_bytes,
                               0, // no local
                               1, // no ack
                               0, // exclusive
                               amqp_empty_table);
            auto rep = amqp_get_rpc_reply(cxn);
            respond(rep);
        }
        amqp_envelope_t env;
        struct timeval timeout;
        while (!should_stop_)
        {
            timeout.tv_sec = 0;
            timeout.tv_usec = 500000;
            amqp_maybe_release_buffers(cxn);
            auto rep = amqp_consume_message(cxn, &env, &timeout, 0);
            if (rep.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION && rep.library_error == AMQP_STATUS_TIMEOUT)
                continue;
            respond(rep);
            other_face_->send(reinterpret_cast<uint8_t*>(env.message.body.bytes), env.message.body.len);
            amqp_destroy_envelope(&env);
        }
    }
    catch (const std::exception& e)
    {
        CHUCHO_FATAL_L("Error occurred with broker: " << e.what());
        callback_of_death_();
    }
    if (cxn != nullptr)
    {
        amqp_channel_close(cxn, YELLA_CHANNEL, AMQP_REPLY_SUCCESS);
        amqp_connection_close(cxn, AMQP_REPLY_SUCCESS);
        amqp_destroy_connection(cxn);
    }
    CHUCHO_INFO_L_STR("Message queue receiver is ending");
}

void rabbit_mq_face::run(face* other_face,
                         std::function<void()> callback_of_death)
{
    mq_face::run(other_face, callback_of_death);
    receiver_ = std::thread(&rabbit_mq_face::receiver_main, this);
}

void rabbit_mq_face::send(const std::uint8_t* const msg, std::size_t len)
{
    auto found = senders_.find(std::this_thread::get_id());
    if (found == senders_.end())
    {
        found = senders_.emplace(std::piecewise_construct,
                                 std::forward_as_tuple(std::this_thread::get_id()),
                                 std::forward_as_tuple(*this)).first;
    }
    found->second.send(msg, len);
}

rabbit_mq_face::sender::sender(rabbit_mq_face& rmf)
    : config_(rmf.config_),
      cxn_(rmf.create_connection())
{
}

rabbit_mq_face::sender::~sender()
{
    amqp_channel_close(cxn_, YELLA_CHANNEL, AMQP_REPLY_SUCCESS);
    amqp_connection_close(cxn_, AMQP_REPLY_SUCCESS);
    amqp_destroy_connection(cxn_);
}

void rabbit_mq_face::sender::send(const std::uint8_t* const msg, std::size_t len)
{
    auto parcel = yella::fb::Getparcel(msg);
    auto type = parcel->type();
    if (type == nullptr)
    {
        throw std::runtime_error("No type included in the parcel");
    }
    else
    {
        auto tstr = type->str();
        auto xbytes = amqp_cstring_bytes(tstr.c_str());
        auto found = exchanges_.find(tstr);
        if (found == exchanges_.end())
        {
            amqp_exchange_declare(cxn_,
                                  YELLA_CHANNEL,
                                  xbytes,
                                  amqp_cstring_bytes("amq.fanout"),
                                  0, // passive
                                  1, // durable
                                  0, // auto-delete
                                  0, // internal
                                  amqp_empty_table);
            amqp_rpc_reply_t rep = amqp_get_rpc_reply(cxn_);
            respond(rep);
            if (config_.bind_exchanges())
            {
                amqp_queue_declare(cxn_,
                                   YELLA_CHANNEL,
                                   xbytes,
                                   0, // passive
                                   1, // durable
                                   0, // exclusive
                                   0, // auto-delete
                                   amqp_empty_table);
                rep = amqp_get_rpc_reply(cxn_);
                respond(rep);
                amqp_queue_bind(cxn_,
                                YELLA_CHANNEL,
                                xbytes,
                                xbytes,
                                amqp_empty_bytes,
                                amqp_empty_table);
                rep = amqp_get_rpc_reply(cxn_);
                respond(rep);
            }
            exchanges_.insert(tstr);
        }
        amqp_bytes_t mbytes;
        mbytes.bytes = const_cast<uint8_t*>(msg);
        mbytes.len = len;
        int rc = amqp_basic_publish(cxn_,
                                    YELLA_CHANNEL,
                                    xbytes,
                                    amqp_empty_bytes,
                                    0, // mandatory
                                    0, // immediate
                                    nullptr, // properties
                                    mbytes);
        if (rc != AMQP_STATUS_OK)
            throw std::runtime_error(std::string("Error publishing to RabbitMQ: ") + amqp_error_string2(rc));
    }
}

}

}<|endoftext|>


<commit_before>/*
 * DIPlib 3.0
 * This file contains binary propagation.
 *
 * (c)2017, Erik Schuitema.
 * Based on original DIPlib code: (c)1995-2014, Delft University of Technology.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "diplib.h"
#include "diplib/binary.h"
#include "diplib/neighborlist.h"
#include "diplib/iterators.h"
#include "binary_support.h"

namespace dip {

void BinaryPropagation(
   Image const& c_inSeed,
   Image const& c_inMask,
   Image& out,
   dip::sint connectivity,
   dip::uint iterations,
   String const& s_edgeCondition
) {
   // Verify that the mask image is forged, scalar and binary
   DIP_THROW_IF( !c_inMask.IsForged(), E::IMAGE_NOT_FORGED );
   DIP_THROW_IF( !c_inMask.DataType().IsBinary(), E::IMAGE_NOT_BINARY );
   DIP_THROW_IF( !c_inMask.IsScalar(), E::IMAGE_NOT_SCALAR );

   // If the seed image is not raw, check that it is scalar and binary and of the same size as inMask
   if( c_inSeed.IsForged() ) {
      DIP_THROW_IF( !c_inSeed.DataType().IsBinary(), E::IMAGE_NOT_BINARY );
      DIP_THROW_IF( !c_inSeed.IsScalar(), E::IMAGE_NOT_SCALAR );
      DIP_THROW_IF( c_inMask.Sizes() != c_inSeed.Sizes(), E::SIZES_DONT_MATCH );
   }

   // Check connectivity validity
   dip::uint nDims = c_inMask.Dimensionality();
   DIP_THROW_IF( connectivity > static_cast< dip::sint >( nDims ), E::ILLEGAL_CONNECTIVITY );

   // Edge condition: true means object, false means background
   bool outsideImageIsObject;
   DIP_STACK_TRACE_THIS( outsideImageIsObject = BooleanFromString( s_edgeCondition, S::OBJECT, S::BACKGROUND ));

   // Make out equal to inMask
   Image inMask = c_inMask.QuickCopy(); // temporary copy of input image headers, so we can strip/reforge out
   Image inSeed = c_inSeed.QuickCopy();
   PixelSize pixelSize = inSeed.HasPixelSize() ? inSeed.PixelSize() : inMask.PixelSize();
   if( out.Aliases( inMask )) { // make sure we don't overwrite the mask image
      out.Strip();
   }
   out.ReForge( inMask.Sizes(), 1, DT_BIN );
   // Copy inSeed plane to output plane if it is non-empty, otherwise clear it
   // Operation takes place directly in the output plane.
   if( inSeed.IsForged() ) {
      out.Copy( inSeed );     // if &c_inSeed == &out, we get here too. Copy won't do anything.
   } else {
      out = 0; // No seed data means: initialize all samples with false
   }
   out.SetPixelSize( pixelSize );

   // Zero iterations means: continue until propagation is done
   if( iterations == 0 ) {
      iterations = std::numeric_limits< dip::uint >::max();
   }

   // Bit planes
   constexpr uint8 dataBitmask = 1; // Data mask: the pixel data is in the first 'plane'
   constexpr uint8 maskBitmask = uint8( 1 << 3 );
   constexpr uint8 borderBitmask = uint8( 1 << 2 );
   constexpr uint8 seedBitmask = uint8( 1 << 0 ); // seed bitmask equals data bitmask
   constexpr uint8 maskOrSeedBitmask = seedBitmask | maskBitmask;

   // Use border mask to mark pixels of the image border
   ApplyBinaryBorderMask( out, borderBitmask );

   // Add mask plane to out image
   JointImageIterator< dip::bin, dip::bin > itInMaskOut( { inMask, out } );
   itInMaskOut.OptimizeAndFlatten();
   do {
      if( itInMaskOut.In() ) {
         SetBits( *reinterpret_cast< uint8* >( itInMaskOut.OutPointer() ), maskBitmask );
      }
   } while( ++itInMaskOut );

   // The edge pixel queue
   BinaryFifoQueue edgePixels;

   DIP_START_STACK_TRACE

   // Create arrays with offsets to neighbours for even iterations
   dip::uint iterConnectivity0 = GetAbsBinaryConnectivity( nDims, connectivity, 0 );
   NeighborList neighborList0( { Metric::TypeCode::CONNECTED, iterConnectivity0 }, nDims );
   IntegerArray neighborOffsetsOut0 = neighborList0.ComputeOffsets( out.Strides() );

   // Create arrays with offsets to neighbours for odd iterations
   dip::uint iterConnectivity1 = GetAbsBinaryConnectivity( nDims, connectivity, 1 ); // won't throw
   NeighborList neighborList1( { Metric::TypeCode::CONNECTED, iterConnectivity1 }, nDims );
   IntegerArray neighborOffsetsOut1 = neighborList1.ComputeOffsets( out.Strides() );

   // Initialize the queue by finding all edge pixels of type 'background'
   bool findObjectPixels = false;
   FindBinaryEdgePixels( out, findObjectPixels, neighborList0, neighborOffsetsOut0, dataBitmask, borderBitmask, outsideImageIsObject, edgePixels );

   // First iteration: process all elements in the queue a first time
   dip::uint count = edgePixels.size();
   for( dip::uint jj = 0; jj < count; ++jj ) {
      dip::bin* pPixel = edgePixels.front();
      uint8& pixelByte = *reinterpret_cast< uint8* >( pPixel );
      if(( pixelByte & maskOrSeedBitmask ) == maskBitmask ) {
         SetBits( pixelByte, seedBitmask );
         edgePixels.push_back( pPixel );
      }
      // Remove the front pixel from the queue
      edgePixels.pop_front();
   }

   // Create a coordinates computer for bounds checking of border pixels
   const CoordinatesComputer coordsComputer = out.OffsetToCoordinatesComputer();

   // Second and further iterations. Loop stops if the queue is empty
   for( dip::uint ii = 1; ( ii < iterations ) && !edgePixels.empty(); ++ii ) {
      // Obtain neighbor list and offsets for this iteration
      NeighborList const& neighborList = ( ii & 1 ) == 1 ? neighborList1 : neighborList0;
      IntegerArray const& neighborOffsetsOut = ( ii & 1 ) == 1 ? neighborOffsetsOut1 : neighborOffsetsOut0;

      // Process all elements currently in the queue
      count = edgePixels.size();
      for( dip::uint jj = 0; jj < count; ++jj ) {
         // Get front pixel from the queue
         dip::bin* pPixel = edgePixels.front();
         edgePixels.pop_front();
         uint8& pixelByte = *reinterpret_cast< uint8* >( pPixel );
         bool isBorderPixel = TestAnyBit( pixelByte, borderBitmask );

         // Propagate to all neighbours which are not yet processed
         dip::IntegerArray::const_iterator itNeighborOffset = neighborOffsetsOut.begin();
         for( NeighborList::Iterator itNeighbor = neighborList.begin(); itNeighbor != neighborList.end(); ++itNeighbor, ++itNeighborOffset ) {
            if( !isBorderPixel || itNeighbor.IsInImage( coordsComputer( pPixel - static_cast< dip::bin* >( out.Origin() )), out.Sizes() ) ) { // IsInImage() is not evaluated for non-border pixels
               dip::bin* pNeighbor = pPixel + *itNeighborOffset;
               uint8& neighborByte = *reinterpret_cast< uint8* >( pNeighbor );
               //bool neighborIsObject = neighborByte & dataBitmask;
               // If the neighbor has the mask-bit (means: propagation allowed)
               // but not the seed-bit (means: not yet processed),
               // process this neighbor.
               if(( neighborByte & maskOrSeedBitmask ) == maskBitmask ) {
                  // Propagate to the neighbor pixel
                  SetBits( neighborByte, seedBitmask );
                  // Add neighbor to the queue
                  edgePixels.push_back( pNeighbor );
               }
            }
         }
      }
   }

   DIP_END_STACK_TRACE

   // Final step: pixels have their data bit set iff they have both seed-bit and mask-bit
   // The result is stored in a way that resets all bits except bit 0
   // This means that the border mask is also removed
   ImageIterator< dip::bin > itOut( out );
   itOut.OptimizeAndFlatten();
   do {
      *itOut = TestBits( static_cast< uint8 >( *itOut ), maskOrSeedBitmask );
   } while( ++itOut );
}

} // namespace dip
<commit_msg>dip::BinaryPropagation (and consequently dip::HysteresisThreshold) propagate pixel sizes. Fixes #50.<commit_after>/*
 * DIPlib 3.0
 * This file contains binary propagation.
 *
 * (c)2017, Erik Schuitema.
 * Based on original DIPlib code: (c)1995-2014, Delft University of Technology.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "diplib.h"
#include "diplib/binary.h"
#include "diplib/neighborlist.h"
#include "diplib/iterators.h"
#include "binary_support.h"

namespace dip {

void BinaryPropagation(
   Image const& c_inSeed,
   Image const& c_inMask,
   Image& out,
   dip::sint connectivity,
   dip::uint iterations,
   String const& s_edgeCondition
) {
   // Verify that the mask image is forged, scalar and binary
   DIP_THROW_IF( !c_inMask.IsForged(), E::IMAGE_NOT_FORGED );
   DIP_THROW_IF( !c_inMask.DataType().IsBinary(), E::IMAGE_NOT_BINARY );
   DIP_THROW_IF( !c_inMask.IsScalar(), E::IMAGE_NOT_SCALAR );

   // If the seed image is not raw, check that it is scalar and binary and of the same size as inMask
   if( c_inSeed.IsForged() ) {
      DIP_THROW_IF( !c_inSeed.DataType().IsBinary(), E::IMAGE_NOT_BINARY );
      DIP_THROW_IF( !c_inSeed.IsScalar(), E::IMAGE_NOT_SCALAR );
      DIP_THROW_IF( c_inMask.Sizes() != c_inSeed.Sizes(), E::SIZES_DONT_MATCH );
   }

   // Check connectivity validity
   dip::uint nDims = c_inMask.Dimensionality();
   DIP_THROW_IF( connectivity > static_cast< dip::sint >( nDims ), E::ILLEGAL_CONNECTIVITY );

   // Edge condition: true means object, false means background
   bool outsideImageIsObject;
   DIP_STACK_TRACE_THIS( outsideImageIsObject = BooleanFromString( s_edgeCondition, S::OBJECT, S::BACKGROUND ));

   // Make out equal to inMask
   Image inMask = c_inMask.QuickCopy(); // temporary copy of input image headers, so we can strip/reforge out
   Image inSeed = c_inSeed.QuickCopy();
   PixelSize pixelSize = c_inSeed.HasPixelSize() ? c_inSeed.PixelSize() : c_inMask.PixelSize();
   if( out.Aliases( inMask )) { // make sure we don't overwrite the mask image
      out.Strip();
   }
   out.ReForge( inMask.Sizes(), 1, DT_BIN );
   // Copy inSeed plane to output plane if it is non-empty, otherwise clear it
   // Operation takes place directly in the output plane.
   if( inSeed.IsForged() ) {
      out.Copy( inSeed );     // if &c_inSeed == &out, we get here too. Copy won't do anything.
   } else {
      out = 0; // No seed data means: initialize all samples with false
   }
   out.SetPixelSize( pixelSize );

   // Zero iterations means: continue until propagation is done
   if( iterations == 0 ) {
      iterations = std::numeric_limits< dip::uint >::max();
   }

   // Bit planes
   constexpr uint8 dataBitmask{ 1u }; // Data mask: the pixel data is in the first 'plane'
   constexpr uint8 maskBitmask{ 1u << 3u };
   constexpr uint8 borderBitmask{ 1u << 2u };
   constexpr uint8 seedBitmask{ 1u << 0u }; // seed bitmask equals data bitmask
   constexpr uint8 maskOrSeedBitmask = seedBitmask | maskBitmask;

   // Use border mask to mark pixels of the image border
   ApplyBinaryBorderMask( out, borderBitmask );

   // Add mask plane to out image
   JointImageIterator< dip::bin, dip::bin > itInMaskOut( { inMask, out } );
   itInMaskOut.OptimizeAndFlatten();
   do {
      if( itInMaskOut.In() ) {
         SetBits( *reinterpret_cast< uint8* >( itInMaskOut.OutPointer() ), maskBitmask );
      }
   } while( ++itInMaskOut );

   // The edge pixel queue
   BinaryFifoQueue edgePixels;

   DIP_START_STACK_TRACE

   // Create arrays with offsets to neighbours for even iterations
   dip::uint iterConnectivity0 = GetAbsBinaryConnectivity( nDims, connectivity, 0 );
   NeighborList neighborList0( { Metric::TypeCode::CONNECTED, iterConnectivity0 }, nDims );
   IntegerArray neighborOffsetsOut0 = neighborList0.ComputeOffsets( out.Strides() );

   // Create arrays with offsets to neighbours for odd iterations
   dip::uint iterConnectivity1 = GetAbsBinaryConnectivity( nDims, connectivity, 1 ); // won't throw
   NeighborList neighborList1( { Metric::TypeCode::CONNECTED, iterConnectivity1 }, nDims );
   IntegerArray neighborOffsetsOut1 = neighborList1.ComputeOffsets( out.Strides() );

   // Initialize the queue by finding all edge pixels of type 'background'
   bool findObjectPixels = false;
   FindBinaryEdgePixels( out, findObjectPixels, neighborList0, neighborOffsetsOut0, dataBitmask, borderBitmask, outsideImageIsObject, edgePixels );

   // First iteration: process all elements in the queue a first time
   dip::uint count = edgePixels.size();
   for( dip::uint jj = 0; jj < count; ++jj ) {
      dip::bin* pPixel = edgePixels.front();
      uint8& pixelByte = *reinterpret_cast< uint8* >( pPixel );
      if(( pixelByte & maskOrSeedBitmask ) == maskBitmask ) {
         SetBits( pixelByte, seedBitmask );
         edgePixels.push_back( pPixel );
      }
      // Remove the front pixel from the queue
      edgePixels.pop_front();
   }

   // Create a coordinates computer for bounds checking of border pixels
   const CoordinatesComputer coordsComputer = out.OffsetToCoordinatesComputer();

   // Second and further iterations. Loop stops if the queue is empty
   for( dip::uint ii = 1; ( ii < iterations ) && !edgePixels.empty(); ++ii ) {
      // Obtain neighbor list and offsets for this iteration
      NeighborList const& neighborList = ( ii & 1u ) == 1 ? neighborList1 : neighborList0;
      IntegerArray const& neighborOffsetsOut = ( ii & 1u ) == 1 ? neighborOffsetsOut1 : neighborOffsetsOut0;

      // Process all elements currently in the queue
      count = edgePixels.size();
      for( dip::uint jj = 0; jj < count; ++jj ) {
         // Get front pixel from the queue
         dip::bin* pPixel = edgePixels.front();
         edgePixels.pop_front();
         uint8& pixelByte = *reinterpret_cast< uint8* >( pPixel );
         bool isBorderPixel = TestAnyBit( pixelByte, borderBitmask );

         // Propagate to all neighbours which are not yet processed
         dip::IntegerArray::const_iterator itNeighborOffset = neighborOffsetsOut.begin();
         for( NeighborList::Iterator itNeighbor = neighborList.begin(); itNeighbor != neighborList.end(); ++itNeighbor, ++itNeighborOffset ) {
            if( !isBorderPixel || itNeighbor.IsInImage( coordsComputer( pPixel - static_cast< dip::bin* >( out.Origin() )), out.Sizes() ) ) { // IsInImage() is not evaluated for non-border pixels
               dip::bin* pNeighbor = pPixel + *itNeighborOffset;
               uint8& neighborByte = *reinterpret_cast< uint8* >( pNeighbor );
               //bool neighborIsObject = neighborByte & dataBitmask;
               // If the neighbor has the mask-bit (means: propagation allowed)
               // but not the seed-bit (means: not yet processed),
               // process this neighbor.
               if(( neighborByte & maskOrSeedBitmask ) == maskBitmask ) {
                  // Propagate to the neighbor pixel
                  SetBits( neighborByte, seedBitmask );
                  // Add neighbor to the queue
                  edgePixels.push_back( pNeighbor );
               }
            }
         }
      }
   }

   DIP_END_STACK_TRACE

   // Final step: pixels have their data bit set iff they have both seed-bit and mask-bit
   // The result is stored in a way that resets all bits except bit 0
   // This means that the border mask is also removed
   ImageIterator< dip::bin > itOut( out );
   itOut.OptimizeAndFlatten();
   do {
      *itOut = TestBits( static_cast< uint8 >( *itOut ), maskOrSeedBitmask );
   } while( ++itOut );
}

} // namespace dip
<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexDMIS.cxx
 ** Lexer for DMIS.
  **/
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
// Copyright 2013-2014 by Andreas Tscharner <andy@vis.ethz.ch>
// The License.txt file describes the conditions under which this software may be distributed.

#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>

#include "ILexer.h"
#include "SciLexer.h"
#include "Scintilla.h"

#include "LexerModule.h"
#include "LexAccessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "WordList.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

static const char *const DMISWordListDesc[] = {
	"DMIS Major Words",
	"DMIS Minor Words",
	"Unsupported DMIS Major Words",
	"Unsupported DMIS Minor Words",
	"Keywords for code folding start",
	"Corresponding keywords for code folding end",
	0
};

class LexerDMIS : public ILexer
{
	private:
		char *m_wordListSets;
		WordList m_majorWords;
		WordList m_minorWords;
		WordList m_unsupportedMajor;
		WordList m_unsupportedMinor;
		WordList m_codeFoldingStart;
		WordList m_codeFoldingEnd;

		char * SCI_METHOD UpperCase(char *item);
		void SCI_METHOD InitWordListSets(void);

	public:
		LexerDMIS(void);
		virtual ~LexerDMIS(void);

		int SCI_METHOD Version() const {
			return lvOriginal;
		}

		void SCI_METHOD Release() {
			delete this;
		}

		const char * SCI_METHOD PropertyNames() {
			return NULL;
		}

		int SCI_METHOD PropertyType(const char *) {
			return -1;
		}

		const char * SCI_METHOD DescribeProperty(const char *) {
			return NULL;
		}

		int SCI_METHOD PropertySet(const char *, const char *) {
			return -1;
		}

		int SCI_METHOD WordListSet(int n, const char *wl);

		void * SCI_METHOD PrivateCall(int, void *) {
			return NULL;
		}

		static ILexer *LexerFactoryDMIS() {
			return new LexerDMIS;
		}

		const char * SCI_METHOD DescribeWordListSets();
		void SCI_METHOD Lex(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess);
		void SCI_METHOD Fold(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess);
};

char * SCI_METHOD LexerDMIS::UpperCase(char *item)
{
	char *itemStart;

	itemStart = item;
	while (item && *item) {
		*item = toupper(*item);
		item++;
	};
	return itemStart;
}

void SCI_METHOD LexerDMIS::InitWordListSets(void)
{
	size_t totalLen = 0;

	for (int i=0; DMISWordListDesc[i]; i++) {
		totalLen += strlen(DMISWordListDesc[i]);
		totalLen++;
	};

	totalLen++;
	this->m_wordListSets = new char[totalLen];
	memset(this->m_wordListSets, 0, totalLen);

	for (int i=0; DMISWordListDesc[i]; i++) {
		strcat(this->m_wordListSets, DMISWordListDesc[i]);
		strcat(this->m_wordListSets, "\n");
	};
}

LexerDMIS::LexerDMIS(void) {
	this->InitWordListSets();

	this->m_majorWords.Clear();
	this->m_minorWords.Clear();
	this->m_unsupportedMajor.Clear();
	this->m_unsupportedMinor.Clear();
	this->m_codeFoldingStart.Clear();
	this->m_codeFoldingEnd.Clear();
}

LexerDMIS::~LexerDMIS(void) {
	delete[] this->m_wordListSets;
}

int SCI_METHOD LexerDMIS::WordListSet(int n, const char *wl)
{
	switch (n) {
		case 0:
			this->m_majorWords.Clear();
			this->m_majorWords.Set(wl);
			break;
		case 1:
			this->m_minorWords.Clear();
			this->m_minorWords.Set(wl);
			break;
		case 2:
			this->m_unsupportedMajor.Clear();
			this->m_unsupportedMajor.Set(wl);
			break;
		case 3:
			this->m_unsupportedMinor.Clear();
			this->m_unsupportedMinor.Set(wl);
			break;
		case 4:
			this->m_codeFoldingStart.Clear();
			this->m_codeFoldingStart.Set(wl);
			break;
		case 5:
			this->m_codeFoldingEnd.Clear();
			this->m_codeFoldingEnd.Set(wl);
			break;
		default:
			return -1;
			break;
	}

	return 0;
}

const char * SCI_METHOD LexerDMIS::DescribeWordListSets()
{
	return this->m_wordListSets;
}

void SCI_METHOD LexerDMIS::Lex(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess)
{
	const unsigned int MAX_STR_LEN = 100;

	LexAccessor styler(pAccess);
	StyleContext scCTX(startPos, lengthDoc, initStyle, styler);
	CharacterSet setDMISNumber(CharacterSet::setDigits, ".-+eE");
	CharacterSet setDMISWordStart(CharacterSet::setAlpha, "-234", 0x80, true);
	CharacterSet setDMISWord(CharacterSet::setAlpha);

	bool isIFLine = false;

	for (; scCTX.More(); scCTX.Forward()) {
		if (scCTX.atLineEnd) {
			isIFLine = false;
		};

		switch (scCTX.state) {
			case SCE_DMIS_DEFAULT:
				if (scCTX.Match('$', '$')) {
					scCTX.SetState(SCE_DMIS_COMMENT);
					scCTX.Forward();
				};
				if (scCTX.Match('\'')) {
					scCTX.SetState(SCE_DMIS_STRING);
				};
				if (IsADigit(scCTX.ch) || ((scCTX.Match('-') || scCTX.Match('+')) && IsADigit(scCTX.chNext))) {
					scCTX.SetState(SCE_DMIS_NUMBER);
					break;
				};
				if (setDMISWordStart.Contains(scCTX.ch)) {
					scCTX.SetState(SCE_DMIS_KEYWORD);
				};
				if (scCTX.Match('(') && (!isIFLine)) {
					scCTX.SetState(SCE_DMIS_LABEL);
				};
				break;

			case SCE_DMIS_COMMENT:
				if (scCTX.atLineEnd) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;

			case SCE_DMIS_STRING:
				if (scCTX.Match('\'')) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;

			case SCE_DMIS_NUMBER:
				if (!setDMISNumber.Contains(scCTX.ch)) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;

			case SCE_DMIS_KEYWORD:
				if (!setDMISWord.Contains(scCTX.ch)) {
					char tmpStr[MAX_STR_LEN];
					memset(tmpStr, 0, MAX_STR_LEN*sizeof(char));
					scCTX.GetCurrent(tmpStr, (MAX_STR_LEN-1));
					strncpy(tmpStr, this->UpperCase(tmpStr), (MAX_STR_LEN-1));

					if (this->m_minorWords.InList(tmpStr)) {
						scCTX.ChangeState(SCE_DMIS_MINORWORD);
					};
					if (this->m_majorWords.InList(tmpStr)) {
						isIFLine = (strcmp(tmpStr, "IF") == 0);
						scCTX.ChangeState(SCE_DMIS_MAJORWORD);
					};
					if (this->m_unsupportedMajor.InList(tmpStr)) {
						scCTX.ChangeState(SCE_DMIS_UNSUPPORTED_MAJOR);
					};
					if (this->m_unsupportedMinor.InList(tmpStr)) {
						scCTX.ChangeState(SCE_DMIS_UNSUPPORTED_MINOR);
					};

					if (scCTX.Match('(') && (isIFLine)) {
						scCTX.SetState(SCE_DMIS_LABEL);
					} else {
						scCTX.SetState(SCE_DMIS_DEFAULT);
					};
				};
				break;

			case SCE_DMIS_LABEL:
				if (scCTX.Match(')')) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;
		};
	};
	scCTX.Complete();
}

void SCI_METHOD LexerDMIS::Fold(unsigned int startPos, int lengthDoc, int, IDocument *pAccess)
{
	const int MAX_STR_LEN = 100;

	LexAccessor styler(pAccess);
	unsigned int endPos = startPos + lengthDoc;
	char chNext = styler[startPos];
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	int strPos = 0;
	bool foldWordPossible = false;
	CharacterSet setDMISFoldWord(CharacterSet::setAlpha);
	char *tmpStr;

	tmpStr = new char[MAX_STR_LEN];
	memset(tmpStr, 0, MAX_STR_LEN*sizeof(char));

	for (unsigned int i=startPos; i<endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i+1);

		bool atEOL = ((ch == '\r' && chNext != '\n') || (ch == '\n'));

		if (strPos >= (MAX_STR_LEN-1)) {
			strPos = MAX_STR_LEN-1;
		};

		int style = styler.StyleAt(i);
		bool noFoldPos = ((style == SCE_DMIS_COMMENT) || (style == SCE_DMIS_STRING));

		if (foldWordPossible) {
			if (setDMISFoldWord.Contains(ch)) {
				tmpStr[strPos++] = ch;
			} else {
				tmpStr = this->UpperCase(tmpStr);
				if (this->m_codeFoldingStart.InList(tmpStr) && (!noFoldPos)) {
					levelCurrent++;
				};
				if (this->m_codeFoldingEnd.InList(tmpStr) && (!noFoldPos)) {
					levelCurrent--;
				};
				memset(tmpStr, 0, MAX_STR_LEN*sizeof(char));
				strPos = 0;
				foldWordPossible = false;
			};
		} else {
			if (setDMISFoldWord.Contains(ch)) {
				tmpStr[strPos++] = ch;
				foldWordPossible = true;
			};
		};

		if (atEOL || (i == (endPos-1))) {
			int lev = levelPrev;

			if (levelCurrent > levelPrev) {
				lev |= SC_FOLDLEVELHEADERFLAG;
			};
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			};
			lineCurrent++;
			levelPrev = levelCurrent;
		};
	};
	delete[] tmpStr;
}

LexerModule lmDMIS(SCLEX_DMIS, LexerDMIS::LexerFactoryDMIS, "DMIS", DMISWordListDesc);
<commit_msg>Add missing not sign to fix DMIS label highlighting<commit_after>// Scintilla source code edit control
/** @file LexDMIS.cxx
 ** Lexer for DMIS.
  **/
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
// Copyright 2013-2014 by Andreas Tscharner <andy@vis.ethz.ch>
// The License.txt file describes the conditions under which this software may be distributed.

#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>

#include "ILexer.h"
#include "SciLexer.h"
#include "Scintilla.h"

#include "LexerModule.h"
#include "LexAccessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "WordList.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

static const char *const DMISWordListDesc[] = {
	"DMIS Major Words",
	"DMIS Minor Words",
	"Unsupported DMIS Major Words",
	"Unsupported DMIS Minor Words",
	"Keywords for code folding start",
	"Corresponding keywords for code folding end",
	0
};

class LexerDMIS : public ILexer
{
	private:
		char *m_wordListSets;
		WordList m_majorWords;
		WordList m_minorWords;
		WordList m_unsupportedMajor;
		WordList m_unsupportedMinor;
		WordList m_codeFoldingStart;
		WordList m_codeFoldingEnd;

		char * SCI_METHOD UpperCase(char *item);
		void SCI_METHOD InitWordListSets(void);

	public:
		LexerDMIS(void);
		virtual ~LexerDMIS(void);

		int SCI_METHOD Version() const {
			return lvOriginal;
		}

		void SCI_METHOD Release() {
			delete this;
		}

		const char * SCI_METHOD PropertyNames() {
			return NULL;
		}

		int SCI_METHOD PropertyType(const char *) {
			return -1;
		}

		const char * SCI_METHOD DescribeProperty(const char *) {
			return NULL;
		}

		int SCI_METHOD PropertySet(const char *, const char *) {
			return -1;
		}

		int SCI_METHOD WordListSet(int n, const char *wl);

		void * SCI_METHOD PrivateCall(int, void *) {
			return NULL;
		}

		static ILexer *LexerFactoryDMIS() {
			return new LexerDMIS;
		}

		const char * SCI_METHOD DescribeWordListSets();
		void SCI_METHOD Lex(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess);
		void SCI_METHOD Fold(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess);
};

char * SCI_METHOD LexerDMIS::UpperCase(char *item)
{
	char *itemStart;

	itemStart = item;
	while (item && *item) {
		*item = toupper(*item);
		item++;
	};
	return itemStart;
}

void SCI_METHOD LexerDMIS::InitWordListSets(void)
{
	size_t totalLen = 0;

	for (int i=0; DMISWordListDesc[i]; i++) {
		totalLen += strlen(DMISWordListDesc[i]);
		totalLen++;
	};

	totalLen++;
	this->m_wordListSets = new char[totalLen];
	memset(this->m_wordListSets, 0, totalLen);

	for (int i=0; DMISWordListDesc[i]; i++) {
		strcat(this->m_wordListSets, DMISWordListDesc[i]);
		strcat(this->m_wordListSets, "\n");
	};
}

LexerDMIS::LexerDMIS(void) {
	this->InitWordListSets();

	this->m_majorWords.Clear();
	this->m_minorWords.Clear();
	this->m_unsupportedMajor.Clear();
	this->m_unsupportedMinor.Clear();
	this->m_codeFoldingStart.Clear();
	this->m_codeFoldingEnd.Clear();
}

LexerDMIS::~LexerDMIS(void) {
	delete[] this->m_wordListSets;
}

int SCI_METHOD LexerDMIS::WordListSet(int n, const char *wl)
{
	switch (n) {
		case 0:
			this->m_majorWords.Clear();
			this->m_majorWords.Set(wl);
			break;
		case 1:
			this->m_minorWords.Clear();
			this->m_minorWords.Set(wl);
			break;
		case 2:
			this->m_unsupportedMajor.Clear();
			this->m_unsupportedMajor.Set(wl);
			break;
		case 3:
			this->m_unsupportedMinor.Clear();
			this->m_unsupportedMinor.Set(wl);
			break;
		case 4:
			this->m_codeFoldingStart.Clear();
			this->m_codeFoldingStart.Set(wl);
			break;
		case 5:
			this->m_codeFoldingEnd.Clear();
			this->m_codeFoldingEnd.Set(wl);
			break;
		default:
			return -1;
			break;
	}

	return 0;
}

const char * SCI_METHOD LexerDMIS::DescribeWordListSets()
{
	return this->m_wordListSets;
}

void SCI_METHOD LexerDMIS::Lex(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess)
{
	const unsigned int MAX_STR_LEN = 100;

	LexAccessor styler(pAccess);
	StyleContext scCTX(startPos, lengthDoc, initStyle, styler);
	CharacterSet setDMISNumber(CharacterSet::setDigits, ".-+eE");
	CharacterSet setDMISWordStart(CharacterSet::setAlpha, "-234", 0x80, true);
	CharacterSet setDMISWord(CharacterSet::setAlpha);

	bool isIFLine = false;

	for (; scCTX.More(); scCTX.Forward()) {
		if (scCTX.atLineEnd) {
			isIFLine = false;
		};

		switch (scCTX.state) {
			case SCE_DMIS_DEFAULT:
				if (scCTX.Match('$', '$')) {
					scCTX.SetState(SCE_DMIS_COMMENT);
					scCTX.Forward();
				};
				if (scCTX.Match('\'')) {
					scCTX.SetState(SCE_DMIS_STRING);
				};
				if (IsADigit(scCTX.ch) || ((scCTX.Match('-') || scCTX.Match('+')) && IsADigit(scCTX.chNext))) {
					scCTX.SetState(SCE_DMIS_NUMBER);
					break;
				};
				if (setDMISWordStart.Contains(scCTX.ch)) {
					scCTX.SetState(SCE_DMIS_KEYWORD);
				};
				if (scCTX.Match('(') && (!isIFLine)) {
					scCTX.SetState(SCE_DMIS_LABEL);
				};
				break;

			case SCE_DMIS_COMMENT:
				if (scCTX.atLineEnd) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;

			case SCE_DMIS_STRING:
				if (scCTX.Match('\'')) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;

			case SCE_DMIS_NUMBER:
				if (!setDMISNumber.Contains(scCTX.ch)) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;

			case SCE_DMIS_KEYWORD:
				if (!setDMISWord.Contains(scCTX.ch)) {
					char tmpStr[MAX_STR_LEN];
					memset(tmpStr, 0, MAX_STR_LEN*sizeof(char));
					scCTX.GetCurrent(tmpStr, (MAX_STR_LEN-1));
					strncpy(tmpStr, this->UpperCase(tmpStr), (MAX_STR_LEN-1));

					if (this->m_minorWords.InList(tmpStr)) {
						scCTX.ChangeState(SCE_DMIS_MINORWORD);
					};
					if (this->m_majorWords.InList(tmpStr)) {
						isIFLine = (strcmp(tmpStr, "IF") == 0);
						scCTX.ChangeState(SCE_DMIS_MAJORWORD);
					};
					if (this->m_unsupportedMajor.InList(tmpStr)) {
						scCTX.ChangeState(SCE_DMIS_UNSUPPORTED_MAJOR);
					};
					if (this->m_unsupportedMinor.InList(tmpStr)) {
						scCTX.ChangeState(SCE_DMIS_UNSUPPORTED_MINOR);
					};

					if (scCTX.Match('(') && (!isIFLine)) {
						scCTX.SetState(SCE_DMIS_LABEL);
					} else {
						scCTX.SetState(SCE_DMIS_DEFAULT);
					};
				};
				break;

			case SCE_DMIS_LABEL:
				if (scCTX.Match(')')) {
					scCTX.SetState(SCE_DMIS_DEFAULT);
				};
				break;
		};
	};
	scCTX.Complete();
}

void SCI_METHOD LexerDMIS::Fold(unsigned int startPos, int lengthDoc, int, IDocument *pAccess)
{
	const int MAX_STR_LEN = 100;

	LexAccessor styler(pAccess);
	unsigned int endPos = startPos + lengthDoc;
	char chNext = styler[startPos];
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	int strPos = 0;
	bool foldWordPossible = false;
	CharacterSet setDMISFoldWord(CharacterSet::setAlpha);
	char *tmpStr;

	tmpStr = new char[MAX_STR_LEN];
	memset(tmpStr, 0, MAX_STR_LEN*sizeof(char));

	for (unsigned int i=startPos; i<endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i+1);

		bool atEOL = ((ch == '\r' && chNext != '\n') || (ch == '\n'));

		if (strPos >= (MAX_STR_LEN-1)) {
			strPos = MAX_STR_LEN-1;
		};

		int style = styler.StyleAt(i);
		bool noFoldPos = ((style == SCE_DMIS_COMMENT) || (style == SCE_DMIS_STRING));

		if (foldWordPossible) {
			if (setDMISFoldWord.Contains(ch)) {
				tmpStr[strPos++] = ch;
			} else {
				tmpStr = this->UpperCase(tmpStr);
				if (this->m_codeFoldingStart.InList(tmpStr) && (!noFoldPos)) {
					levelCurrent++;
				};
				if (this->m_codeFoldingEnd.InList(tmpStr) && (!noFoldPos)) {
					levelCurrent--;
				};
				memset(tmpStr, 0, MAX_STR_LEN*sizeof(char));
				strPos = 0;
				foldWordPossible = false;
			};
		} else {
			if (setDMISFoldWord.Contains(ch)) {
				tmpStr[strPos++] = ch;
				foldWordPossible = true;
			};
		};

		if (atEOL || (i == (endPos-1))) {
			int lev = levelPrev;

			if (levelCurrent > levelPrev) {
				lev |= SC_FOLDLEVELHEADERFLAG;
			};
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			};
			lineCurrent++;
			levelPrev = levelCurrent;
		};
	};
	delete[] tmpStr;
}

LexerModule lmDMIS(SCLEX_DMIS, LexerDMIS::LexerFactoryDMIS, "DMIS", DMISWordListDesc);
<|endoftext|>


<commit_before>/*
 *  tree_decomposition.hpp
 *
 *
 *  Created by Andrea Bedini on 14/Jul/09.
 *  Copyright 2009, 2010, 2011 Andrea Bedini.
 *
 *  Distributed under the terms of the GNU General Public License.
 *  The full license is in the file COPYING, distributed as part of
 *  this software.
 *
 */

#ifndef TREE_DECOMPOSITION_HPP
#define TREE_DECOMPOSITION_HPP

#include "tree_decomposition/tree.hh"
#include "utility/smallset.hpp"

#include <boost/foreach.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/range/algorithm/find_first_of.hpp>
#include <boost/property_map/vector_property_map.hpp>

#include <algorithm>
#include <iosfwd>
#include <vector>

namespace tree_decomposition {
  struct bag {
    smallset<unsigned int> vertices;
    std::vector<std::pair<unsigned int, unsigned int> > edges;
  };

  typedef tree<bag> tree_decomposition;

  template<class Graph, class Range>
  tree_decomposition
  build_tree_decomposition(Range const& order, Graph g)
  // please note that g is passed by copy
  {
    using namespace std;
    using namespace boost;

    typedef typename Graph::vertex_descriptor vertex_descriptor;
    typedef typename Graph::edge_descriptor   edge_descriptor;

    vector<vertex_descriptor> vertices(num_vertices(g));

    // list (stable) vertex descriptors in the user-supplied order
    unsigned int i = 0;
    BOOST_FOREACH(unsigned int vi, order)
      vertices[i++] = vertex(vi, g);

    // bags will be indexed by vertex indices
    vector<bag> bags(num_vertices(g));

    // each bag's parent index need also to be stored
    vector<unsigned int> parent(num_vertices(g));

    // we need this map to differenciate between edges added during
    // vertex eliminations from the ones originally present in the
    // graph
    vector_property_map
      < bool
      , typename property_map<Graph, edge_index_t>::type
      > filling_edge(num_edges(g), get(edge_index, g));

    // iterate over these vertices in order
    BOOST_FOREACH(vertex_descriptor v, vertices) {
      // this is the index
      unsigned int vi = get(vertex_index, g, v);

      // add to each vertex bag the vertex (index) vi
      bags[vi].vertices.insert(vi);
      BOOST_FOREACH(edge_descriptor e, out_edges(v, g)) {
	// and add its neighbours
	unsigned int ui = get(vertex_index, g, target(e, g));
	bags[vi].vertices.insert(ui);
	// add the edge to the bag if the edge is 'genuine'
	if (not filling_edge[e])
	  bags[vi].edges.push_back(make_pair(vi, ui));
      }

      // if we find the parent vertex index
      if (out_degree(v, g) > 0) {
	vertex_descriptor u = *find_first_of(vertices, adjacent_vertices(v, g));
	parent[vi] = get(vertex_index, g, u);
      }

      // eliminate vertex, marking filling edges as we add them
      BOOST_FOREACH(vertex_descriptor a, adjacent_vertices(v, g)) {
	BOOST_FOREACH(vertex_descriptor b, adjacent_vertices(v, g)) {
	  if (a != b and not edge(a, b, g).second) {
	    edge_descriptor e = add_edge(a, b, num_edges(g), g).first;
	    filling_edge[e] = true;
	  }
	}
      }
      clear_vertex(v, g);
      remove_vertex(v, g);
    }

    // ok, now we have a list of bags each with a parent index
    // time to unroll the tree
    tree_decomposition tree;

    // again we need a vector of tree nodes (booring!)
    vector<tree_decomposition::iterator> nodes(bags.size());

    // iterate the vertex ordering backward, the first (= last) bag
    // is going to be the tree root
    typename Range::const_reverse_iterator ri = order.rbegin(), ri_end = order.rend();
    nodes[*ri] = tree.set_head(bags[*ri]);
    ++ ri;
    while (ri != ri_end) {
      nodes[*ri] = tree.append_child(nodes[parent[*ri]], bags[*ri]);
      ++ ri;
    }

    return tree;
  }

  unsigned int tree_width(const tree_decomposition& t)
  {
    unsigned int max = 0;
    tree_decomposition::iterator ti;
    for(ti = t.begin(); ti != t.end(); ++ti)
      if (ti->vertices.size() > max)
	max = ti->vertices.size();
    return max - 1;
  }

  void print_tree(std::ostream& o,
		  tree_decomposition::sibling_iterator ti,
		  const tree_decomposition& t)
  {
    o << "( ";
    BOOST_FOREACH(unsigned int v, ti->vertices) {
      o << v << " ";
    }
    if (not ti->edges.empty()) {
      o << "| ";
      std::pair<unsigned, unsigned> p;
      BOOST_FOREACH(p, ti->edges) {
	o << p.first << "-" << p.second << " ";
      }
    }
    o << ") ";
    tree_decomposition::sibling_iterator
      sib = t.begin(ti), sib_end = t.end(ti);
    if (sib != sib_end) {
      o << "{ ";
      for (; sib != sib_end; ++sib)
	print_tree(o, sib, t);
      o << "} ";
    }
  }

  std::ostream& operator<<(std::ostream& o, const tree_decomposition& t)
  {
    print_tree(o, t.begin(), t);
    return o;
  }
}

#endif
<commit_msg>Fix stupid bug causing edge indexes collision.<commit_after>/*
 *  tree_decomposition.hpp
 *
 *
 *  Created by Andrea Bedini on 14/Jul/09.
 *  Copyright 2009, 2010, 2011 Andrea Bedini.
 *
 *  Distributed under the terms of the GNU General Public License.
 *  The full license is in the file COPYING, distributed as part of
 *  this software.
 *
 */

#ifndef TREE_DECOMPOSITION_HPP
#define TREE_DECOMPOSITION_HPP

#include "tree_decomposition/tree.hh"
#include "utility/smallset.hpp"

#include <boost/foreach.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/range/algorithm/find_first_of.hpp>
#include <boost/property_map/vector_property_map.hpp>

#include <algorithm>
#include <iosfwd>
#include <vector>

namespace tree_decomposition {
  struct bag {
    smallset<unsigned int> vertices;
    std::vector<std::pair<unsigned int, unsigned int> > edges;
  };

  typedef tree<bag> tree_decomposition;

  template<class Graph, class Range>
  tree_decomposition
  build_tree_decomposition(Range const& order, Graph g)
  // please note that g is passed by copy
  {
    using namespace std;
    using namespace boost;

    typedef typename Graph::vertex_descriptor vertex_descriptor;
    typedef typename Graph::edge_descriptor   edge_descriptor;

    vector<vertex_descriptor> vertices(num_vertices(g));
    unsigned int free_edge_index = num_edges(g);

    // list (stable) vertex descriptors in the user-supplied order
    unsigned int i = 0;
    BOOST_FOREACH(unsigned int vi, order)
      vertices[i++] = vertex(vi, g);

    // bags will be indexed by vertex indices
    vector<bag> bags(num_vertices(g));

    // each bag's parent index need also to be stored
    vector<unsigned int> parent(num_vertices(g));

    // we need this map to differenciate between edges added during
    // vertex eliminations from the ones originally present in the
    // graph
    vector_property_map
      < bool
      , typename property_map<Graph, edge_index_t>::type
      > filling_edge(num_edges(g), get(edge_index, g));

    // iterate over these vertices in order
    BOOST_FOREACH(vertex_descriptor v, vertices) {
      // this is the index
      unsigned int vi = get(vertex_index, g, v);

      // add to each vertex bag the vertex (index) vi
      bags[vi].vertices.insert(vi);
      BOOST_FOREACH(edge_descriptor e, out_edges(v, g)) {
	// and add its neighbours
	unsigned int ui = get(vertex_index, g, target(e, g));
	bags[vi].vertices.insert(ui);
	// add the edge to the bag if the edge is 'genuine'
	if (not filling_edge[e])
	  bags[vi].edges.push_back(make_pair(vi, ui));
      }

      // if we find the parent vertex index
      if (out_degree(v, g) > 0) {
	vertex_descriptor u = *find_first_of(vertices, adjacent_vertices(v, g));
	parent[vi] = get(vertex_index, g, u);
      }

      // eliminate vertex, marking filling edges as we add them
      BOOST_FOREACH(vertex_descriptor a, adjacent_vertices(v, g)) {
	BOOST_FOREACH(vertex_descriptor b, adjacent_vertices(v, g)) {
	  if (a != b and not edge(a, b, g).second) {
	    edge_descriptor e = add_edge(a, b, free_edge_index++, g).first;
	    filling_edge[e] = true;
	  }
	}
      }
      clear_vertex(v, g);
      remove_vertex(v, g);
    }

    // ok, now we have a list of bags each with a parent index
    // time to unroll the tree
    tree_decomposition tree;

    // again we need a vector of tree nodes (booring!)
    vector<tree_decomposition::iterator> nodes(bags.size());

    // iterate the vertex ordering backward, the first (= last) bag
    // is going to be the tree root
    typename Range::const_reverse_iterator ri = order.rbegin(), ri_end = order.rend();
    nodes[*ri] = tree.set_head(bags[*ri]);
    ++ ri;
    while (ri != ri_end) {
      nodes[*ri] = tree.append_child(nodes[parent[*ri]], bags[*ri]);
      ++ ri;
    }

    return tree;
  }

  unsigned int tree_width(const tree_decomposition& t)
  {
    unsigned int max = 0;
    tree_decomposition::iterator ti;
    for(ti = t.begin(); ti != t.end(); ++ti)
      if (ti->vertices.size() > max)
	max = ti->vertices.size();
    return max - 1;
  }

  void print_tree(std::ostream& o,
		  tree_decomposition::sibling_iterator ti,
		  const tree_decomposition& t)
  {
    o << "( ";
    BOOST_FOREACH(unsigned int v, ti->vertices) {
      o << v << " ";
    }
    if (not ti->edges.empty()) {
      o << "| ";
      std::pair<unsigned, unsigned> p;
      BOOST_FOREACH(p, ti->edges) {
	o << p.first << "-" << p.second << " ";
      }
    }
    o << ") ";
    tree_decomposition::sibling_iterator
      sib = t.begin(ti), sib_end = t.end(ti);
    if (sib != sib_end) {
      o << "{ ";
      for (; sib != sib_end; ++sib)
	print_tree(o, sib, t);
      o << "} ";
    }
  }

  std::ostream& operator<<(std::ostream& o, const tree_decomposition& t)
  {
    print_tree(o, t.begin(), t);
    return o;
  }
}

#endif
<|endoftext|>


<commit_before>/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

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

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2015 Live Networks, Inc.  All rights reserved.
// Abstract class for parsing a byte stream
// Implementation

#include "StreamParser.hh"

#include <string.h>
#include <stdlib.h>

#define BANK_SIZE 150000

void StreamParser::flushInput() {
  fCurParserIndex = fSavedParserIndex = 0;
  fSavedRemainingUnparsedBits = fRemainingUnparsedBits = 0;
  fTotNumValidBytes = 0;
}

StreamParser::StreamParser(FramedSource* inputSource,
			   FramedSource::onCloseFunc* onInputCloseFunc,
			   void* onInputCloseClientData,
			   clientContinueFunc* clientContinueFunc,
			   void* clientContinueClientData)
  : fInputSource(inputSource), fClientOnInputCloseFunc(onInputCloseFunc),
    fClientOnInputCloseClientData(onInputCloseClientData),
    fClientContinueFunc(clientContinueFunc),
    fClientContinueClientData(clientContinueClientData),
    fSavedParserIndex(0), fSavedRemainingUnparsedBits(0),
    fCurParserIndex(0), fRemainingUnparsedBits(0),
    fTotNumValidBytes(0), fHaveSeenEOF(False) {
  fBank[0] = new unsigned char[BANK_SIZE];
  fBank[1] = new unsigned char[BANK_SIZE];
  fCurBankNum = 0;
  fCurBank = fBank[fCurBankNum];

  fLastSeenPresentationTime.tv_sec = 0; fLastSeenPresentationTime.tv_usec = 0;
}

StreamParser::~StreamParser() {
  delete[] fBank[0]; delete[] fBank[1];
}

void StreamParser::saveParserState() {
  fSavedParserIndex = fCurParserIndex;
  fSavedRemainingUnparsedBits = fRemainingUnparsedBits;
}

void StreamParser::restoreSavedParserState() {
  fCurParserIndex = fSavedParserIndex;
  fRemainingUnparsedBits = fSavedRemainingUnparsedBits;
}

void StreamParser::skipBits(unsigned numBits) {
  if (numBits <= fRemainingUnparsedBits) {
    fRemainingUnparsedBits -= numBits;
  } else {
    numBits -= fRemainingUnparsedBits;

    unsigned numBytesToExamine = (numBits+7)/8; // round up
    ensureValidBytes(numBytesToExamine);
    fCurParserIndex += numBytesToExamine;

    fRemainingUnparsedBits = 8*numBytesToExamine - numBits;
  }
}

unsigned StreamParser::getBits(unsigned numBits) {
  if (numBits <= fRemainingUnparsedBits) {
    unsigned char lastByte = *lastParsed();
    lastByte >>= (fRemainingUnparsedBits - numBits);
    fRemainingUnparsedBits -= numBits;

    return (unsigned)lastByte &~ ((~0u)<<numBits);
  } else {
    unsigned char lastByte;
    if (fRemainingUnparsedBits > 0) {
      lastByte = *lastParsed();
    } else {
      lastByte = 0;
    }

    unsigned remainingBits = numBits - fRemainingUnparsedBits; // > 0

    // For simplicity, read the next 4 bytes, even though we might not
    // need all of them here:
    unsigned result = test4Bytes();

    result >>= (32 - remainingBits);
    result |= (lastByte << remainingBits);
    if (numBits < 32) result &=~ ((~0u)<<numBits);

    unsigned const numRemainingBytes = (remainingBits+7)/8;
    fCurParserIndex += numRemainingBytes;
    fRemainingUnparsedBits = 8*numRemainingBytes - remainingBits;

    return result;
  }
}

unsigned StreamParser::bankSize() const {
  return BANK_SIZE;
}

#define NO_MORE_BUFFERED_INPUT 1

void StreamParser::ensureValidBytes1(unsigned numBytesNeeded) {
  // We need to read some more bytes from the input source.
  // First, clarify how much data to ask for:
  unsigned maxInputFrameSize = fInputSource->maxFrameSize();
  if (maxInputFrameSize > numBytesNeeded) numBytesNeeded = maxInputFrameSize;

  // First, check whether these new bytes would overflow the current
  // bank.  If so, start using a new bank now.
  if (fCurParserIndex + numBytesNeeded > BANK_SIZE) {
    // Swap banks, but save any still-needed bytes from the old bank:
    unsigned numBytesToSave = fTotNumValidBytes - fSavedParserIndex;
    unsigned char const* from = &curBank()[fSavedParserIndex];

    fCurBankNum = (fCurBankNum + 1)%2;
    fCurBank = fBank[fCurBankNum];
    memmove(curBank(), from, numBytesToSave);
    fCurParserIndex = fCurParserIndex - fSavedParserIndex;
    fSavedParserIndex = 0;
    fTotNumValidBytes = numBytesToSave;
  }

  // ASSERT: fCurParserIndex + numBytesNeeded > fTotNumValidBytes
  //      && fCurParserIndex + numBytesNeeded <= BANK_SIZE
  if (fCurParserIndex + numBytesNeeded > BANK_SIZE) {
    // If this happens, it means that we have too much saved parser state.
    // To fix this, increase BANK_SIZE as appropriate.
    fInputSource->envir() << "StreamParser internal error ("
			  << fCurParserIndex << " + "
			  << numBytesNeeded << " > "
			  << BANK_SIZE << ")\n";
    fInputSource->envir().internalError();
  }

  // Try to read as many new bytes as will fit in the current bank:
  unsigned maxNumBytesToRead = BANK_SIZE - fTotNumValidBytes;
  fInputSource->getNextFrame(&curBank()[fTotNumValidBytes],
			     maxNumBytesToRead,
			     afterGettingBytes, this,
			     onInputClosure, this);

  throw NO_MORE_BUFFERED_INPUT;
}

void StreamParser::afterGettingBytes(void* clientData,
				     unsigned numBytesRead,
				     unsigned /*numTruncatedBytes*/,
				     struct timeval presentationTime,
				     unsigned /*durationInMicroseconds*/){
  StreamParser* parser = (StreamParser*)clientData;
  if (parser != NULL) parser->afterGettingBytes1(numBytesRead, presentationTime);
}

void StreamParser::afterGettingBytes1(unsigned numBytesRead, struct timeval presentationTime) {
  // Sanity check: Make sure we didn't get too many bytes for our bank:
  if (fTotNumValidBytes + numBytesRead > BANK_SIZE) {
    fInputSource->envir()
      << "StreamParser::afterGettingBytes() warning: read "
      << numBytesRead << " bytes; expected no more than "
      << BANK_SIZE - fTotNumValidBytes << "\n";
  }

  fLastSeenPresentationTime = presentationTime;

  unsigned char* ptr = &curBank()[fTotNumValidBytes];
  fTotNumValidBytes += numBytesRead;

  // Continue our original calling source where it left off:
  restoreSavedParserState();
      // Sigh... this is a crock; things would have been a lot simpler
      // here if we were using threads, with synchronous I/O...
  fClientContinueFunc(fClientContinueClientData, ptr, numBytesRead, presentationTime);
}

void StreamParser::onInputClosure(void* clientData) {
  StreamParser* parser = (StreamParser*)clientData;
  if (parser != NULL) parser->onInputClosure1();
}

void StreamParser::onInputClosure1() {
  if (!fHaveSeenEOF) {
    // We're hitting EOF for the first time.  Set our 'EOF' flag, and continue parsing, as if we'd just read 0 bytes of data.
    // This allows the parser to re-parse any remaining unparsed data (perhaps while testing for EOF at the end):
    fHaveSeenEOF = True;
    afterGettingBytes1(0, fLastSeenPresentationTime);
  } else {
    // We're hitting EOF for the second time.  Now, we handle the source input closure:
    fHaveSeenEOF = False;
    if (fClientOnInputCloseFunc != NULL) (*fClientOnInputCloseFunc)(fClientOnInputCloseClientData);
  }
}
<commit_msg>Increase bank size.<commit_after>/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

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

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2015 Live Networks, Inc.  All rights reserved.
// Abstract class for parsing a byte stream
// Implementation

#include "StreamParser.hh"

#include <string.h>
#include <stdlib.h>
#if 0
#define BANK_SIZE 150000
#else

#define BANK_SIZE (2 * 1024 * 1024)
#endif

void StreamParser::flushInput() {
  fCurParserIndex = fSavedParserIndex = 0;
  fSavedRemainingUnparsedBits = fRemainingUnparsedBits = 0;
  fTotNumValidBytes = 0;
}

StreamParser::StreamParser(FramedSource* inputSource,
			   FramedSource::onCloseFunc* onInputCloseFunc,
			   void* onInputCloseClientData,
			   clientContinueFunc* clientContinueFunc,
			   void* clientContinueClientData)
  : fInputSource(inputSource), fClientOnInputCloseFunc(onInputCloseFunc),
    fClientOnInputCloseClientData(onInputCloseClientData),
    fClientContinueFunc(clientContinueFunc),
    fClientContinueClientData(clientContinueClientData),
    fSavedParserIndex(0), fSavedRemainingUnparsedBits(0),
    fCurParserIndex(0), fRemainingUnparsedBits(0),
    fTotNumValidBytes(0), fHaveSeenEOF(False) {
  fBank[0] = new unsigned char[BANK_SIZE];
  fBank[1] = new unsigned char[BANK_SIZE];
  fCurBankNum = 0;
  fCurBank = fBank[fCurBankNum];

  fLastSeenPresentationTime.tv_sec = 0; fLastSeenPresentationTime.tv_usec = 0;
}

StreamParser::~StreamParser() {
  delete[] fBank[0]; delete[] fBank[1];
}

void StreamParser::saveParserState() {
  fSavedParserIndex = fCurParserIndex;
  fSavedRemainingUnparsedBits = fRemainingUnparsedBits;
}

void StreamParser::restoreSavedParserState() {
  fCurParserIndex = fSavedParserIndex;
  fRemainingUnparsedBits = fSavedRemainingUnparsedBits;
}

void StreamParser::skipBits(unsigned numBits) {
  if (numBits <= fRemainingUnparsedBits) {
    fRemainingUnparsedBits -= numBits;
  } else {
    numBits -= fRemainingUnparsedBits;

    unsigned numBytesToExamine = (numBits+7)/8; // round up
    ensureValidBytes(numBytesToExamine);
    fCurParserIndex += numBytesToExamine;

    fRemainingUnparsedBits = 8*numBytesToExamine - numBits;
  }
}

unsigned StreamParser::getBits(unsigned numBits) {
  if (numBits <= fRemainingUnparsedBits) {
    unsigned char lastByte = *lastParsed();
    lastByte >>= (fRemainingUnparsedBits - numBits);
    fRemainingUnparsedBits -= numBits;

    return (unsigned)lastByte &~ ((~0u)<<numBits);
  } else {
    unsigned char lastByte;
    if (fRemainingUnparsedBits > 0) {
      lastByte = *lastParsed();
    } else {
      lastByte = 0;
    }

    unsigned remainingBits = numBits - fRemainingUnparsedBits; // > 0

    // For simplicity, read the next 4 bytes, even though we might not
    // need all of them here:
    unsigned result = test4Bytes();

    result >>= (32 - remainingBits);
    result |= (lastByte << remainingBits);
    if (numBits < 32) result &=~ ((~0u)<<numBits);

    unsigned const numRemainingBytes = (remainingBits+7)/8;
    fCurParserIndex += numRemainingBytes;
    fRemainingUnparsedBits = 8*numRemainingBytes - remainingBits;

    return result;
  }
}

unsigned StreamParser::bankSize() const {
  return BANK_SIZE;
}

#define NO_MORE_BUFFERED_INPUT 1

void StreamParser::ensureValidBytes1(unsigned numBytesNeeded) {
  // We need to read some more bytes from the input source.
  // First, clarify how much data to ask for:
  unsigned maxInputFrameSize = fInputSource->maxFrameSize();
  if (maxInputFrameSize > numBytesNeeded) numBytesNeeded = maxInputFrameSize;

  // First, check whether these new bytes would overflow the current
  // bank.  If so, start using a new bank now.
  if (fCurParserIndex + numBytesNeeded > BANK_SIZE) {
    // Swap banks, but save any still-needed bytes from the old bank:
    unsigned numBytesToSave = fTotNumValidBytes - fSavedParserIndex;
    unsigned char const* from = &curBank()[fSavedParserIndex];

    fCurBankNum = (fCurBankNum + 1)%2;
    fCurBank = fBank[fCurBankNum];
    memmove(curBank(), from, numBytesToSave);
    fCurParserIndex = fCurParserIndex - fSavedParserIndex;
    fSavedParserIndex = 0;
    fTotNumValidBytes = numBytesToSave;
  }

  // ASSERT: fCurParserIndex + numBytesNeeded > fTotNumValidBytes
  //      && fCurParserIndex + numBytesNeeded <= BANK_SIZE
  if (fCurParserIndex + numBytesNeeded > BANK_SIZE) {
    // If this happens, it means that we have too much saved parser state.
    // To fix this, increase BANK_SIZE as appropriate.
    fInputSource->envir() << "StreamParser internal error ("
			  << fCurParserIndex << " + "
			  << numBytesNeeded << " > "
			  << BANK_SIZE << ")\n";
    fInputSource->envir().internalError();
  }

  // Try to read as many new bytes as will fit in the current bank:
  unsigned maxNumBytesToRead = BANK_SIZE - fTotNumValidBytes;
  fInputSource->getNextFrame(&curBank()[fTotNumValidBytes],
			     maxNumBytesToRead,
			     afterGettingBytes, this,
			     onInputClosure, this);

  throw NO_MORE_BUFFERED_INPUT;
}

void StreamParser::afterGettingBytes(void* clientData,
				     unsigned numBytesRead,
				     unsigned /*numTruncatedBytes*/,
				     struct timeval presentationTime,
				     unsigned /*durationInMicroseconds*/){
  StreamParser* parser = (StreamParser*)clientData;
  if (parser != NULL) parser->afterGettingBytes1(numBytesRead, presentationTime);
}

void StreamParser::afterGettingBytes1(unsigned numBytesRead, struct timeval presentationTime) {
  // Sanity check: Make sure we didn't get too many bytes for our bank:
  if (fTotNumValidBytes + numBytesRead > BANK_SIZE) {
    fInputSource->envir()
      << "StreamParser::afterGettingBytes() warning: read "
      << numBytesRead << " bytes; expected no more than "
      << BANK_SIZE - fTotNumValidBytes << "\n";
  }

  fLastSeenPresentationTime = presentationTime;

  unsigned char* ptr = &curBank()[fTotNumValidBytes];
  fTotNumValidBytes += numBytesRead;

  // Continue our original calling source where it left off:
  restoreSavedParserState();
      // Sigh... this is a crock; things would have been a lot simpler
      // here if we were using threads, with synchronous I/O...
  fClientContinueFunc(fClientContinueClientData, ptr, numBytesRead, presentationTime);
}

void StreamParser::onInputClosure(void* clientData) {
  StreamParser* parser = (StreamParser*)clientData;
  if (parser != NULL) parser->onInputClosure1();
}

void StreamParser::onInputClosure1() {
  if (!fHaveSeenEOF) {
    // We're hitting EOF for the first time.  Set our 'EOF' flag, and continue parsing, as if we'd just read 0 bytes of data.
    // This allows the parser to re-parse any remaining unparsed data (perhaps while testing for EOF at the end):
    fHaveSeenEOF = True;
    afterGettingBytes1(0, fLastSeenPresentationTime);
  } else {
    // We're hitting EOF for the second time.  Now, we handle the source input closure:
    fHaveSeenEOF = False;
    if (fClientOnInputCloseFunc != NULL) (*fClientOnInputCloseFunc)(fClientOnInputCloseClientData);
  }
}
<|endoftext|>