<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: pe_iface.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 18:48:46 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <precomp.h>
#include <s2_luidl/pe_iface.hxx>

// NOT FULLY DEFINED SERVICES
#include <ary/idl/i_interface.hxx>
#include <ary/idl/i_gate.hxx>
#include <ary/idl/ip_ce.hxx>
#include <ary_i/codeinf2.hxx>
#include <s2_luidl/pe_func2.hxx>
#include <s2_luidl/pe_attri.hxx>
#include <s2_luidl/pe_type2.hxx>
#include <s2_luidl/tk_keyw.hxx>
#include <s2_luidl/tk_ident.hxx>
#include <s2_luidl/tk_punct.hxx>
#include <adc_cl.hxx>

namespace csi
{
namespace uidl
{

#ifdef DF
#undef DF
#endif
#define DF  &PE_Interface::On_Default

PE_Interface::F_TOK
PE_Interface::aDispatcher[PE_Interface::e_STATES_MAX][PE_Interface::tt_MAX] =
        {   { DF, DF, DF, DF, DF },  // e_none
            { &PE_Interface::On_need_uik_MetaType,
                 DF, DF, DF, DF },  // need_uik
            { DF, &PE_Interface::On_uik_Identifier,
                     &PE_Interface::On_uik_Punctuation,
                         DF, DF },  // uik
            { &PE_Interface::On_need_ident_MetaType,
                 DF, DF, DF, DF },  // need_ident
            { DF, &PE_Interface::On_ident_Identifier,
                     &PE_Interface::On_ident_Punctuation,
                         DF, DF },  // ident
            { &PE_Interface::On_need_interface_MetaType,
                 DF, DF, DF, DF },  // need_interface
            { DF, &PE_Interface::On_need_name_Identifer,
                    DF, DF, DF },  // need_name
            { DF, DF, &PE_Interface::On_wait_for_base_Punctuation,
                        DF, DF },  // wait_for_base
            { DF, DF, DF, DF, DF },  // in_base
            { DF, DF, &PE_Interface::On_need_curlbr_open_Punctuation,
                          DF, DF },  // need_curlbr_open
            { &PE_Interface::On_std_Metatype,
                  &PE_Interface::On_std_GotoFunction,
                      &PE_Interface::On_std_Punctuation,
                          &PE_Interface::On_std_GotoFunction,
                              &PE_Interface::On_std_Stereotype },  // e_std
            { DF, DF, DF, DF, DF },  // in_function
            { DF, DF, DF, DF, DF },  // in_attribute
            { DF, DF, &PE_Interface::On_need_finish_Punctuation,
                          DF, DF },  // need_finish
            { DF, DF, DF, DF, DF }   // in_base_interface
        };

inline void
PE_Interface::CallHandler( const char *     i_sTokenText,
                           E_TokenType      i_eTokenType )
    { (this->*aDispatcher[eState][i_eTokenType])(i_sTokenText); }

PE_Interface::PE_Interface()
    :   eState(e_none),
        sData_Name(),
        bIsPreDeclaration(false),
        pCurInterface(0),
        nCurInterface(0),
        pPE_Function(0),
        pPE_Attribute(0),
        pPE_Type(0),
        nCurParsed_Base(0),
        bOptional(false)
{
    pPE_Function    = new PE_Function(nCurInterface);
    pPE_Type        = new PE_Type(nCurParsed_Base);
    pPE_Attribute   = new PE_Attribute(nCurInterface);
}

void
PE_Interface::EstablishContacts( UnoIDL_PE *                io_pParentPE,
                                 ary::n22::Repository &     io_rRepository,
                                 TokenProcessing_Result &   o_rResult )
{
    UnoIDL_PE::EstablishContacts(io_pParentPE,io_rRepository,o_rResult);
    pPE_Function->EstablishContacts(this,io_rRepository,o_rResult);
    pPE_Type->EstablishContacts(this,io_rRepository,o_rResult);
    pPE_Attribute->EstablishContacts(this,io_rRepository,o_rResult);
}

PE_Interface::~PE_Interface()
{
}

void
PE_Interface::ProcessToken( const Token & i_rToken )
{
    i_rToken.Trigger(*this);
}

void
PE_Interface::Process_MetaType( const TokMetaType & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_metatype );
}

void
PE_Interface::Process_Identifier( const TokIdentifier & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_identifier );
}

void
PE_Interface::Process_Punctuation( const TokPunctuation & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_punctuation );
}

void
PE_Interface::Process_NameSeparator()
{
    CallHandler( "", tt_startoftype );
}

void
PE_Interface::Process_BuiltInType( const TokBuiltInType & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_startoftype );
}

void
PE_Interface::Process_TypeModifier( const TokTypeModifier & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_startoftype );
}

void
PE_Interface::Process_Stereotype( const TokStereotype & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_stereotype );
}

void
PE_Interface::Process_Default()
{
    SetResult(done, stay);
}

void
PE_Interface::On_need_uik_MetaType(const char * i_sText)
{
    // Deprecated, data will be ignored
    SetResult(done, stay);
    eState = uik;
}

void
PE_Interface::On_uik_Identifier(const char * i_sText)
{
    // Deprecated, data will be ignored
    SetResult(done, stay);
}

void
PE_Interface::On_uik_Punctuation(const char * i_sText)
{
    // Deprecated, data will be ignored
    SetResult(done, stay);
    if (strcmp(",",i_sText) == 0)
    {
        eState = need_ident;
    }
}

void
PE_Interface::On_need_ident_MetaType(const char * i_sText)
{
    SetResult(done, stay);
    eState = ident;
}

void
PE_Interface::On_ident_Identifier(const char * i_sText)
{
    SetResult(done, stay);
}

void
PE_Interface::On_ident_Punctuation(const char * i_sText)
{
    SetResult(done, stay);
    if (strcmp(")",i_sText) == 0)
    {
        eState = need_interface;
    }
}

void
PE_Interface::On_need_interface_MetaType(const char * i_sText)
{
    SetResult(done, stay);
    eState = need_name;
}

void
PE_Interface::On_need_name_Identifer(const char * i_sText)
{
    SetResult(done, stay);
    sData_Name = i_sText;
    eState = wait_for_base;
}

void
PE_Interface::On_wait_for_base_Punctuation(const char * i_sText)
{
    if (i_sText[0] != ';')
    {
        switch (i_sText[0])
        {
            case ':':
                SetResult(done, push_sure, pPE_Type.Ptr());
                eState = in_base;
                break;
            case '{':
                store_Interface();

                SetResult(done,stay);
                eState = e_std;
                break;
            default:
                SetResult(not_done, pop_failure);
                eState = e_none;
        }   // end switch
    }
    else
    {
        bIsPreDeclaration = true;
        SetResult(done, pop_success);
        eState = e_none;
    }
}

void
PE_Interface::On_need_curlbr_open_Punctuation(const char * i_sText)
{
    if (i_sText[0] == '{')
    {
        store_Interface();

        SetResult(done, stay);
        eState = e_std;
    }
    else
        csv_assert(false);
}

void
PE_Interface::On_std_Metatype(const char * i_sText)
{
    if (strcmp(i_sText,"attribute") ==  0)
        On_std_GotoAttribute(i_sText);
    else if (strcmp(i_sText,"interface") ==  0)
        On_std_GotoBaseInterface(i_sText);
    else
        On_std_GotoFunction(i_sText);
}

void
PE_Interface::On_std_Punctuation(const char * i_sText)
{
    switch (i_sText[0])
    {
        case '}':
            SetResult(done, stay);
            eState = need_finish;
            break;
        case ';':   // Appears after base interface declarations.
            SetResult(done, stay);
            break;
        default:
            SetResult(not_done, pop_failure);
            eState = e_none;
    }   // end switch
}

void
PE_Interface::On_std_Stereotype(const char * i_sText)
{
    if (strcmp(i_sText,"oneway") ==  0)
        On_std_GotoFunction(i_sText);
    else if (    strcmp(i_sText,"readonly") ==  0
              OR strcmp(i_sText,"bound") ==  0 )
        On_std_GotoAttribute(i_sText);
    else
        SetResult(not_done, pop_failure);
}

void
PE_Interface::On_std_GotoFunction(const char * )
{
    SetResult(not_done, push_sure, pPE_Function.Ptr());
    eState = in_function;
}

void
PE_Interface::On_std_GotoAttribute(const char * )
{
        SetResult(not_done, push_sure, pPE_Attribute.Ptr());
        eState = in_attribute;
}

void
PE_Interface::On_std_GotoBaseInterface(const char * )
{
       SetResult(done, push_sure, pPE_Type.Ptr());
    eState = in_base_interface;
}

void
PE_Interface::On_need_finish_Punctuation(const char * i_sText)
{
    switch (i_sText[0])
    {
        case ';':
            SetResult(done, pop_success);
            eState = e_none;
            break;
        default:
            SetResult(not_done, pop_failure);
            eState = e_none;
    }   // end switch
}

void
PE_Interface::On_Default(const char * i_sText)
{
    SetResult(not_done, pop_failure);
}

void
PE_Interface::InitData()
{
    eState = need_interface;

    sData_Name.clear();
    bIsPreDeclaration = false;
    pCurInterface = 0;
    nCurInterface = 0;
    nCurParsed_Base = 0;
    bOptional = false;
}

void
PE_Interface::TransferData()
{
    if (NOT bIsPreDeclaration)
    {
        csv_assert(sData_Name.size() > 0);
        csv_assert(nCurInterface.IsValid());
    }
    else
    {
         sData_Name.clear();
         pCurInterface = 0;
        nCurInterface = 0;
    }

    eState = e_none;
}

void
PE_Interface::ReceiveData()
{
    switch (eState)
    {
        case in_base:
                eState = need_curlbr_open;
                break;
        case in_function:
                eState = e_std;
                break;
        case in_attribute:
                eState = e_std;
                break;
        case in_base_interface:
                if (bOptional)
                {
                    pPE_Type->SetOptional();
                    bOptional = false;
                }
                pCurInterface->Add_Base(
                                    nCurParsed_Base,
                                    pPE_Type->ReleaseDocu());
                nCurParsed_Base = 0;
                eState = e_std;
                break;
        default:
            csv_assert(false);
    }
}

UnoIDL_PE &
PE_Interface::MyPE()
{
     return *this;
}

void
PE_Interface::store_Interface()
{
    pCurInterface = & Gate().Ces().Store_Interface(
                                        CurNamespace().CeId(),
                                        sData_Name,
                                        nCurParsed_Base );
    nCurInterface = pCurInterface->CeId();
    PassDocuAt(*pCurInterface);
}

}   // namespace uidl
}   // namespace csi
<commit_msg>INTEGRATION: CWS warnings01 (1.7.4); FILE MERGED 2005/10/18 08:36:18 np 1.7.4.1: #i53898#<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: pe_iface.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 12:07:15 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <precomp.h>
#include <s2_luidl/pe_iface.hxx>

// NOT FULLY DEFINED SERVICES
#include <ary/idl/i_interface.hxx>
#include <ary/idl/i_gate.hxx>
#include <ary/idl/ip_ce.hxx>
#include <ary_i/codeinf2.hxx>
#include <s2_luidl/pe_func2.hxx>
#include <s2_luidl/pe_attri.hxx>
#include <s2_luidl/pe_type2.hxx>
#include <s2_luidl/tk_keyw.hxx>
#include <s2_luidl/tk_ident.hxx>
#include <s2_luidl/tk_punct.hxx>
#include <adc_cl.hxx>

namespace csi
{
namespace uidl
{

#ifdef DF
#undef DF
#endif
#define DF  &PE_Interface::On_Default

PE_Interface::F_TOK
PE_Interface::aDispatcher[PE_Interface::e_STATES_MAX][PE_Interface::tt_MAX] =
        {   { DF, DF, DF, DF, DF },  // e_none
            { &PE_Interface::On_need_uik_MetaType,
                 DF, DF, DF, DF },  // need_uik
            { DF, &PE_Interface::On_uik_Identifier,
                     &PE_Interface::On_uik_Punctuation,
                         DF, DF },  // uik
            { &PE_Interface::On_need_ident_MetaType,
                 DF, DF, DF, DF },  // need_ident
            { DF, &PE_Interface::On_ident_Identifier,
                     &PE_Interface::On_ident_Punctuation,
                         DF, DF },  // ident
            { &PE_Interface::On_need_interface_MetaType,
                 DF, DF, DF, DF },  // need_interface
            { DF, &PE_Interface::On_need_name_Identifer,
                    DF, DF, DF },  // need_name
            { DF, DF, &PE_Interface::On_wait_for_base_Punctuation,
                        DF, DF },  // wait_for_base
            { DF, DF, DF, DF, DF },  // in_base
            { DF, DF, &PE_Interface::On_need_curlbr_open_Punctuation,
                          DF, DF },  // need_curlbr_open
            { &PE_Interface::On_std_Metatype,
                  &PE_Interface::On_std_GotoFunction,
                      &PE_Interface::On_std_Punctuation,
                          &PE_Interface::On_std_GotoFunction,
                              &PE_Interface::On_std_Stereotype },  // e_std
            { DF, DF, DF, DF, DF },  // in_function
            { DF, DF, DF, DF, DF },  // in_attribute
            { DF, DF, &PE_Interface::On_need_finish_Punctuation,
                          DF, DF },  // need_finish
            { DF, DF, DF, DF, DF }   // in_base_interface
        };

inline void
PE_Interface::CallHandler( const char *     i_sTokenText,
                           E_TokenType      i_eTokenType )
    { (this->*aDispatcher[eState][i_eTokenType])(i_sTokenText); }

PE_Interface::PE_Interface()
    :   eState(e_none),
        sData_Name(),
        bIsPreDeclaration(false),
        pCurInterface(0),
        nCurInterface(0),
        pPE_Function(0),
        pPE_Attribute(0),
        pPE_Type(0),
        nCurParsed_Base(0),
        bOptional(false)
{
    pPE_Function    = new PE_Function(nCurInterface);
    pPE_Type        = new PE_Type(nCurParsed_Base);
    pPE_Attribute   = new PE_Attribute(nCurInterface);
}

void
PE_Interface::EstablishContacts( UnoIDL_PE *                io_pParentPE,
                                 ary::n22::Repository &     io_rRepository,
                                 TokenProcessing_Result &   o_rResult )
{
    UnoIDL_PE::EstablishContacts(io_pParentPE,io_rRepository,o_rResult);
    pPE_Function->EstablishContacts(this,io_rRepository,o_rResult);
    pPE_Type->EstablishContacts(this,io_rRepository,o_rResult);
    pPE_Attribute->EstablishContacts(this,io_rRepository,o_rResult);
}

PE_Interface::~PE_Interface()
{
}

void
PE_Interface::ProcessToken( const Token & i_rToken )
{
    i_rToken.Trigger(*this);
}

void
PE_Interface::Process_MetaType( const TokMetaType & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_metatype );
}

void
PE_Interface::Process_Identifier( const TokIdentifier & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_identifier );
}

void
PE_Interface::Process_Punctuation( const TokPunctuation & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_punctuation );
}

void
PE_Interface::Process_NameSeparator()
{
    CallHandler( "", tt_startoftype );
}

void
PE_Interface::Process_BuiltInType( const TokBuiltInType & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_startoftype );
}

void
PE_Interface::Process_TypeModifier( const TokTypeModifier & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_startoftype );
}

void
PE_Interface::Process_Stereotype( const TokStereotype & i_rToken )
{
    CallHandler( i_rToken.Text(), tt_stereotype );
}

void
PE_Interface::Process_Default()
{
    SetResult(done, stay);
}

void
PE_Interface::On_need_uik_MetaType(const char *)
{
    // Deprecated, data will be ignored
    SetResult(done, stay);
    eState = uik;
}

void
PE_Interface::On_uik_Identifier(const char *)
{
    // Deprecated, data will be ignored
    SetResult(done, stay);
}

void
PE_Interface::On_uik_Punctuation(const char * i_sText)
{
    // Deprecated, data will be ignored
    SetResult(done, stay);
    if (strcmp(",",i_sText) == 0)
    {
        eState = need_ident;
    }
}

void
PE_Interface::On_need_ident_MetaType(const char *)
{
    SetResult(done, stay);
    eState = ident;
}

void
PE_Interface::On_ident_Identifier(const char *)
{
    SetResult(done, stay);
}

void
PE_Interface::On_ident_Punctuation(const char * i_sText)
{
    SetResult(done, stay);
    if (strcmp(")",i_sText) == 0)
    {
        eState = need_interface;
    }
}

void
PE_Interface::On_need_interface_MetaType(const char *)
{
    SetResult(done, stay);
    eState = need_name;
}

void
PE_Interface::On_need_name_Identifer(const char * i_sText)
{
    SetResult(done, stay);
    sData_Name = i_sText;
    eState = wait_for_base;
}

void
PE_Interface::On_wait_for_base_Punctuation(const char * i_sText)
{
    if (i_sText[0] != ';')
    {
        switch (i_sText[0])
        {
            case ':':
                SetResult(done, push_sure, pPE_Type.Ptr());
                eState = in_base;
                break;
            case '{':
                store_Interface();

                SetResult(done,stay);
                eState = e_std;
                break;
            default:
                SetResult(not_done, pop_failure);
                eState = e_none;
        }   // end switch
    }
    else
    {
        bIsPreDeclaration = true;
        SetResult(done, pop_success);
        eState = e_none;
    }
}

void
PE_Interface::On_need_curlbr_open_Punctuation(const char * i_sText)
{
    if (i_sText[0] == '{')
    {
        store_Interface();

        SetResult(done, stay);
        eState = e_std;
    }
    else
        csv_assert(false);
}

void
PE_Interface::On_std_Metatype(const char * i_sText)
{
    if (strcmp(i_sText,"attribute") ==  0)
        On_std_GotoAttribute(i_sText);
    else if (strcmp(i_sText,"interface") ==  0)
        On_std_GotoBaseInterface(i_sText);
    else
        On_std_GotoFunction(i_sText);
}

void
PE_Interface::On_std_Punctuation(const char * i_sText)
{
    switch (i_sText[0])
    {
        case '}':
            SetResult(done, stay);
            eState = need_finish;
            break;
        case ';':   // Appears after base interface declarations.
            SetResult(done, stay);
            break;
        default:
            SetResult(not_done, pop_failure);
            eState = e_none;
    }   // end switch
}

void
PE_Interface::On_std_Stereotype(const char * i_sText)
{
    if (strcmp(i_sText,"oneway") ==  0)
        On_std_GotoFunction(i_sText);
    else if (    strcmp(i_sText,"readonly") ==  0
              OR strcmp(i_sText,"bound") ==  0 )
        On_std_GotoAttribute(i_sText);
    else
        SetResult(not_done, pop_failure);
}

void
PE_Interface::On_std_GotoFunction(const char * )
{
    SetResult(not_done, push_sure, pPE_Function.Ptr());
    eState = in_function;
}

void
PE_Interface::On_std_GotoAttribute(const char * )
{
        SetResult(not_done, push_sure, pPE_Attribute.Ptr());
        eState = in_attribute;
}

void
PE_Interface::On_std_GotoBaseInterface(const char * )
{
       SetResult(done, push_sure, pPE_Type.Ptr());
    eState = in_base_interface;
}

void
PE_Interface::On_need_finish_Punctuation(const char * i_sText)
{
    switch (i_sText[0])
    {
        case ';':
            SetResult(done, pop_success);
            eState = e_none;
            break;
        default:
            SetResult(not_done, pop_failure);
            eState = e_none;
    }   // end switch
}

void
PE_Interface::On_Default(const char *)
{
    SetResult(not_done, pop_failure);
}

void
PE_Interface::InitData()
{
    eState = need_interface;

    sData_Name.clear();
    bIsPreDeclaration = false;
    pCurInterface = 0;
    nCurInterface = 0;
    nCurParsed_Base = 0;
    bOptional = false;
}

void
PE_Interface::TransferData()
{
    if (NOT bIsPreDeclaration)
    {
        csv_assert(sData_Name.size() > 0);
        csv_assert(nCurInterface.IsValid());
    }
    else
    {
         sData_Name.clear();
         pCurInterface = 0;
        nCurInterface = 0;
    }

    eState = e_none;
}

void
PE_Interface::ReceiveData()
{
    switch (eState)
    {
        case in_base:
                eState = need_curlbr_open;
                break;
        case in_function:
                eState = e_std;
                break;
        case in_attribute:
                eState = e_std;
                break;
        case in_base_interface:
                if (bOptional)
                {
                    pPE_Type->SetOptional();
                    bOptional = false;
                }
                pCurInterface->Add_Base(
                                    nCurParsed_Base,
                                    pPE_Type->ReleaseDocu());
                nCurParsed_Base = 0;
                eState = e_std;
                break;
        default:
            csv_assert(false);
    }
}

UnoIDL_PE &
PE_Interface::MyPE()
{
     return *this;
}

void
PE_Interface::store_Interface()
{
    pCurInterface = & Gate().Ces().Store_Interface(
                                        CurNamespace().CeId(),
                                        sData_Name,
                                        nCurParsed_Base );
    nCurInterface = pCurInterface->CeId();
    PassDocuAt(*pCurInterface);
}

}   // namespace uidl
}   // namespace csi
<|endoftext|>


<commit_before>#pragma once

#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
#include "geometry/distance.hpp"

#include "base/math.hpp"

#include "std/vector.hpp"
#include "std/algorithm.hpp"
#include "std/type_traits.hpp"

namespace m2
{
  namespace detail
  {
    struct DefEqualFloat
    {
      // 1e-9 is two orders of magnitude more accurate than our OSM source data.
      static double constexpr kPrecision = 1e-9;

      template <class TPoint>
      bool EqualPoints(TPoint const & p1, TPoint const & p2) const
      {
        static_assert(std::is_floating_point<typename TPoint::value_type>::value, "");

        return my::AlmostEqualAbs(p1.x, p2.x, static_cast<typename TPoint::value_type>(kPrecision)) &&
               my::AlmostEqualAbs(p1.y, p2.y, static_cast<typename TPoint::value_type>(kPrecision));
      }
      template <class TCoord>
      bool EqualZeroSquarePrecision(TCoord val) const
      {
        static_assert(std::is_floating_point<TCoord>::value, "");

        return my::AlmostEqualAbs(val, 0.0, kPrecision * kPrecision);
      }
      inline bool IsAlmostBetween(double val, double left, double right) const
      {
        return ((val >= left - kPrecision) && (val <= right + kPrecision)) ||
               ((val <= left + kPrecision) && (val >= right - kPrecision));
      }
    };

    struct DefEqualInt
    {
      template <class TPoint>
      bool EqualPoints(TPoint const & p1, TPoint const & p2) const
      {
        return p1 == p2;
      }
      template <class TCoord>
      bool EqualZeroSquarePrecision(TCoord val) const
      {
        return val == 0;
      }
      inline bool IsAlmostBetween(double val, double left, double right) const
      {
        return ((val >= left) && (val <= right)) ||
               ((val <= left) && (val >= right));
      }
    };

    template <int floating> struct TraitsType;
    template <> struct TraitsType<1>
    {
      typedef DefEqualFloat EqualType;
      typedef double BigType;
    };
    template <> struct TraitsType<0>
    {
      typedef DefEqualInt EqualType;
      typedef int64_t BigType;
    };
  }

  template <class PointT>
  class Region
  {
    template <class TArchive, class TPoint>
    friend TArchive & operator << (TArchive & ar, Region<TPoint> const & region);
    template <class TArchive, class TPoint>
    friend TArchive & operator >> (TArchive & ar, Region<TPoint> & region);

  public:
    typedef PointT ValueT;
    typedef typename PointT::value_type CoordT;

  private:
    typedef vector<PointT> ContainerT;
    typedef detail::TraitsType<is_floating_point<CoordT>::value> TraitsT;

  public:
    /// @name Needed for boost region concept.
    //@{
    typedef typename ContainerT::const_iterator IteratorT;
    IteratorT Begin() const { return m_points.begin(); }
    IteratorT End() const { return m_points.end(); }
    size_t Size() const { return m_points.size(); }
    //@}

  public:
    Region() = default;

    explicit Region(vector<PointD> && points) : m_points(move(points))
    {
      CalcLimitRect();
    }

    template <class IterT>
    Region(IterT first, IterT last) : m_points(first, last)
    {
      CalcLimitRect();
    }

    template <class IterT>
    void Assign(IterT first, IterT last)
    {
      m_points.assign(first, last);
      CalcLimitRect();
    }

    template <class IterT, class Fn>
    void AssignEx(IterT first, IterT last, Fn fn)
    {
      m_points.reserve(distance(first, last));

      while (first != last)
        m_points.push_back(fn(*first++));

      CalcLimitRect();
    }

    void AddPoint(PointT const & pt)
    {
      m_points.push_back(pt);
      m_rect.Add(pt);
    }

    template <class TFunctor>
    void ForEachPoint(TFunctor toDo) const
    {
      for_each(m_points.begin(), m_points.end(), toDo);
    }

    inline m2::Rect<CoordT> const & GetRect() const { return m_rect; }
    inline size_t GetPointsCount() const { return m_points.size(); }
    inline bool IsValid() const { return GetPointsCount() > 2; }

    void Swap(Region<PointT> & rhs)
    {
      m_points.swap(rhs.m_points);
      std::swap(m_rect, rhs.m_rect);
    }

    ContainerT Data() const { return m_points; }

    template <class EqualF>
    inline bool IsIntersect(CoordT const & x11, CoordT const & y11, CoordT const & x12, CoordT const & y12,
                            CoordT const & x21, CoordT const & y21, CoordT const & x22, CoordT const & y22,
                            EqualF equalF, PointT & pt) const
    {
      if (!((y12 - y11) * (x22 - x21) - (x12 - x11) * (y22-y21)))
        return false;
      double v = ((x12 - x11) * (y21 - y11) + (y12 - y11) * (x11 - x21)) / ((y12 - y11) * (x22 - x21) - (x12 - x11) * (y22 - y21));
      PointT p(x21 + (x22 - x21) * v, y21 + (y22 - y21) * v);

      if (!equalF.IsAlmostBetween(p.x, x11, x12))
        return false;
      if (!equalF.IsAlmostBetween(p.x, x21, x22))
        return false;
      if (!equalF.IsAlmostBetween(p.y, y11, y12))
        return false;
      if (!equalF.IsAlmostBetween(p.y, y21, y22))
        return false;

      pt = p;
      return true;
    }

    inline bool IsIntersect(PointT const & p1, PointT const & p2, PointT const & p3, PointT const & p4 , PointT & pt) const
    {
      return IsIntersect(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, typename TraitsT::EqualType(), pt);
    }

  public:

    /// Taken from Computational Geometry in C and modified
    template <class EqualF>
    bool Contains(PointT const & pt, EqualF equalF) const
    {
      if (!m_rect.IsPointInside(pt))
        return false;

      int rCross = 0; /* number of right edge/ray crossings */
      int lCross = 0; /* number of left edge/ray crossings */

      size_t const numPoints = m_points.size();

      typedef typename TraitsT::BigType BigCoordT;
      typedef Point<BigCoordT> BigPointT;

      BigPointT prev = BigPointT(m_points[numPoints - 1]) - BigPointT(pt);
      for (size_t i = 0; i < numPoints; ++i)
      {
        if (equalF.EqualPoints(m_points[i], pt))
          return true;

        BigPointT const curr = BigPointT(m_points[i]) - BigPointT(pt);

        bool const rCheck = ((curr.y > 0) != (prev.y > 0));
        bool const lCheck = ((curr.y < 0) != (prev.y < 0));

        if (rCheck || lCheck)
        {
          ASSERT_NOT_EQUAL ( curr.y, prev.y, () );

          BigCoordT const delta = prev.y - curr.y;
          BigCoordT const cp = CrossProduct(curr, prev);

          // Squared precision is needed here because of comparison between cross product of two
          // vectors and zero. It's impossible to compare them relatively, so they're compared
          // absolutely, and, as cross product is proportional to product of lengths of both
          // operands precision must be squared too.
          if (!equalF.EqualZeroSquarePrecision(cp))
          {
            bool const PrevGreaterCurr = delta > 0.0;

            if (rCheck && ((cp > 0) == PrevGreaterCurr)) ++rCross;
            if (lCheck && ((cp > 0) != PrevGreaterCurr)) ++lCross;
          }
        }

        prev = curr;
      }

      /* q on the edge if left and right cross are not the same parity. */
      if ((rCross & 1) != (lCross & 1))
        return true;  // on the edge

      /* q inside if an odd number of crossings. */
      if (rCross & 1)
        return true;  // inside
      else
        return false; // outside
    }

    bool Contains(PointT const & pt) const
    {
      return Contains(pt, typename TraitsT::EqualType());
    }

    /// Finds point of intersection with the section.
    bool FindIntersection(PointT const & point1, PointT const & point2, PointT & result) const
    {
      if (m_points.empty())
        return false;
      PointT const * prev = &m_points.back();
      for (PointT const & curr : m_points)
      {
        if (IsIntersect(point1, point2, *prev, curr, result))
          return true;
        prev = &curr;
      }
      return false;
    }

    /// Slow check that point lies at the border.
    template <class EqualF>
    bool AtBorder(PointT const & pt, double const delta, EqualF equalF) const
    {
      if (!m_rect.IsPointInside(pt))
        return false;

      const double squareDelta = delta*delta;
      size_t const numPoints = m_points.size();

      PointT prev = m_points[numPoints - 1];
      DistanceToLineSquare<PointT> distance;
      for (size_t i = 0; i < numPoints; ++i)
      {
        PointT const curr = m_points[i];

        // Borders often have same points with ways
        if (equalF.EqualPoints(m_points[i], pt))
          return true;

        distance.SetBounds(prev, curr);
        if (distance(pt) < squareDelta)
          return true;

        prev = curr;
      }

      return false; // Point lies outside the border.
    }

    bool AtBorder(PointT const & pt, double const delta) const
    {
      return AtBorder(pt, delta, typename TraitsT::EqualType());
    }

  private:
    void CalcLimitRect()
    {
      m_rect.MakeEmpty();
      for (size_t i = 0; i < m_points.size(); ++i)
        m_rect.Add(m_points[i]);
    }

    ContainerT m_points;
    m2::Rect<CoordT> m_rect;

    template <class T> friend string DebugPrint(Region<T> const &);
  };

  template <class PointT>
  void swap(Region<PointT> & r1, Region<PointT> & r2)
  {
    r1.Swap(r2);
  }

  template <class TArchive, class PointT>
  TArchive & operator >> (TArchive & ar, Region<PointT> & region)
  {
    ar >> region.m_rect;
    ar >> region.m_points;
    return ar;
  }

  template <class TArchive, class PointT>
  TArchive & operator << (TArchive & ar, Region<PointT> const & region)
  {
    ar << region.m_rect;
    ar << region.m_points;
    return ar;
  }

  template <class PointT>
  inline string DebugPrint(Region<PointT> const & r)
  {
    return (DebugPrint(r.m_rect) + ::DebugPrint(r.m_points));
  }

  typedef Region<m2::PointD> RegionD;
  typedef Region<m2::PointI> RegionI;
  typedef Region<m2::PointU> RegionU;
}
<commit_msg>PR fix.<commit_after>#pragma once

#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
#include "geometry/distance.hpp"

#include "base/math.hpp"

#include "std/vector.hpp"
#include "std/algorithm.hpp"
#include "std/type_traits.hpp"

namespace m2
{
  namespace detail
  {
    struct DefEqualFloat
    {
      // 1e-9 is two orders of magnitude more accurate than our OSM source data.
      static double constexpr kPrecision = 1e-9;

      template <class TPoint>
      bool EqualPoints(TPoint const & p1, TPoint const & p2) const
      {
        static_assert(std::is_floating_point<typename TPoint::value_type>::value, "");

        return my::AlmostEqualAbs(p1.x, p2.x, static_cast<typename TPoint::value_type>(kPrecision)) &&
               my::AlmostEqualAbs(p1.y, p2.y, static_cast<typename TPoint::value_type>(kPrecision));
      }
      template <class TCoord>
      bool EqualZeroSquarePrecision(TCoord val) const
      {
        static_assert(std::is_floating_point<TCoord>::value, "");

        return my::AlmostEqualAbs(val, 0.0, kPrecision * kPrecision);
      }
      // Determines if value of a val lays between a p1 and a p2 values with some precision.
      inline bool IsAlmostBetween(double val, double p1, double p2) const
      {
        return (val >= p1 - kPrecision && val <= p2 + kPrecision) ||
               (val <= p1 + kPrecision && val >= p2 - kPrecision);
      }
    };

    struct DefEqualInt
    {
      template <class TPoint>
      bool EqualPoints(TPoint const & p1, TPoint const & p2) const
      {
        return p1 == p2;
      }
      template <class TCoord>
      bool EqualZeroSquarePrecision(TCoord val) const
      {
        return val == 0;
      }
      inline bool IsAlmostBetween(double val, double left, double right) const
      {
        return (val >= left && val <= right) ||
               (val <= left && val >= right);
      }
    };

    template <int floating> struct TraitsType;
    template <> struct TraitsType<1>
    {
      typedef DefEqualFloat EqualType;
      typedef double BigType;
    };
    template <> struct TraitsType<0>
    {
      typedef DefEqualInt EqualType;
      typedef int64_t BigType;
    };
  }

  template <class PointT>
  class Region
  {
    template <class TArchive, class TPoint>
    friend TArchive & operator << (TArchive & ar, Region<TPoint> const & region);
    template <class TArchive, class TPoint>
    friend TArchive & operator >> (TArchive & ar, Region<TPoint> & region);

  public:
    typedef PointT ValueT;
    typedef typename PointT::value_type CoordT;

  private:
    typedef vector<PointT> ContainerT;
    typedef detail::TraitsType<is_floating_point<CoordT>::value> TraitsT;

  public:
    /// @name Needed for boost region concept.
    //@{
    typedef typename ContainerT::const_iterator IteratorT;
    IteratorT Begin() const { return m_points.begin(); }
    IteratorT End() const { return m_points.end(); }
    size_t Size() const { return m_points.size(); }
    //@}

  public:
    Region() = default;

    explicit Region(vector<PointD> && points) : m_points(move(points))
    {
      CalcLimitRect();
    }

    template <class IterT>
    Region(IterT first, IterT last) : m_points(first, last)
    {
      CalcLimitRect();
    }

    template <class IterT>
    void Assign(IterT first, IterT last)
    {
      m_points.assign(first, last);
      CalcLimitRect();
    }

    template <class IterT, class Fn>
    void AssignEx(IterT first, IterT last, Fn fn)
    {
      m_points.reserve(distance(first, last));

      while (first != last)
        m_points.push_back(fn(*first++));

      CalcLimitRect();
    }

    void AddPoint(PointT const & pt)
    {
      m_points.push_back(pt);
      m_rect.Add(pt);
    }

    template <class TFunctor>
    void ForEachPoint(TFunctor toDo) const
    {
      for_each(m_points.begin(), m_points.end(), toDo);
    }

    inline m2::Rect<CoordT> const & GetRect() const { return m_rect; }
    inline size_t GetPointsCount() const { return m_points.size(); }
    inline bool IsValid() const { return GetPointsCount() > 2; }

    void Swap(Region<PointT> & rhs)
    {
      m_points.swap(rhs.m_points);
      std::swap(m_rect, rhs.m_rect);
    }

    ContainerT Data() const { return m_points; }

    template <class TEqualF>
    inline bool IsIntersect(CoordT const & x11, CoordT const & y11, CoordT const & x12, CoordT const & y12,
                            CoordT const & x21, CoordT const & y21, CoordT const & x22, CoordT const & y22,
                            TEqualF equalF, PointT & pt) const
    {
      double const divider = ((y12 - y11) * (x22 - x21) - (x12 - x11) * (y22-y21));
      if (equalF.EqualZeroSquarePrecision(divider))
        return false;
      double v = ((x12 - x11) * (y21 - y11) + (y12 - y11) * (x11 - x21)) / divider;
      PointT p(x21 + (x22 - x21) * v, y21 + (y22 - y21) * v);

      if (!equalF.IsAlmostBetween(p.x, x11, x12))
        return false;
      if (!equalF.IsAlmostBetween(p.x, x21, x22))
        return false;
      if (!equalF.IsAlmostBetween(p.y, y11, y12))
        return false;
      if (!equalF.IsAlmostBetween(p.y, y21, y22))
        return false;

      pt = p;
      return true;
    }

    inline bool IsIntersect(PointT const & p1, PointT const & p2, PointT const & p3, PointT const & p4 , PointT & pt) const
    {
      return IsIntersect(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, typename TraitsT::EqualType(), pt);
    }

  public:

    /// Taken from Computational Geometry in C and modified
    template <class TEqualF>
    bool Contains(PointT const & pt, TEqualF equalF) const
    {
      if (!m_rect.IsPointInside(pt))
        return false;

      int rCross = 0; /* number of right edge/ray crossings */
      int lCross = 0; /* number of left edge/ray crossings */

      size_t const numPoints = m_points.size();

      typedef typename TraitsT::BigType BigCoordT;
      typedef Point<BigCoordT> BigPointT;

      BigPointT prev = BigPointT(m_points[numPoints - 1]) - BigPointT(pt);
      for (size_t i = 0; i < numPoints; ++i)
      {
        if (equalF.EqualPoints(m_points[i], pt))
          return true;

        BigPointT const curr = BigPointT(m_points[i]) - BigPointT(pt);

        bool const rCheck = ((curr.y > 0) != (prev.y > 0));
        bool const lCheck = ((curr.y < 0) != (prev.y < 0));

        if (rCheck || lCheck)
        {
          ASSERT_NOT_EQUAL ( curr.y, prev.y, () );

          BigCoordT const delta = prev.y - curr.y;
          BigCoordT const cp = CrossProduct(curr, prev);

          // Squared precision is needed here because of comparison between cross product of two
          // vectors and zero. It's impossible to compare them relatively, so they're compared
          // absolutely, and, as cross product is proportional to product of lengths of both
          // operands precision must be squared too.
          if (!equalF.EqualZeroSquarePrecision(cp))
          {
            bool const PrevGreaterCurr = delta > 0.0;

            if (rCheck && ((cp > 0) == PrevGreaterCurr)) ++rCross;
            if (lCheck && ((cp > 0) != PrevGreaterCurr)) ++lCross;
          }
        }

        prev = curr;
      }

      /* q on the edge if left and right cross are not the same parity. */
      if ((rCross & 1) != (lCross & 1))
        return true;  // on the edge

      /* q inside if an odd number of crossings. */
      if (rCross & 1)
        return true;  // inside
      else
        return false; // outside
    }

    bool Contains(PointT const & pt) const
    {
      return Contains(pt, typename TraitsT::EqualType());
    }

    /// Finds point of intersection with the section.
    bool FindIntersection(PointT const & point1, PointT const & point2, PointT & result) const
    {
      if (m_points.empty())
        return false;
      PointT const * prev = &m_points.back();
      for (PointT const & curr : m_points)
      {
        if (IsIntersect(point1, point2, *prev, curr, result))
          return true;
        prev = &curr;
      }
      return false;
    }

    /// Slow check that point lies at the border.
    template <class TEqualF>
    bool AtBorder(PointT const & pt, double const delta, TEqualF equalF) const
    {
      if (!m_rect.IsPointInside(pt))
        return false;

      const double squareDelta = delta*delta;
      size_t const numPoints = m_points.size();

      PointT prev = m_points[numPoints - 1];
      DistanceToLineSquare<PointT> distance;
      for (size_t i = 0; i < numPoints; ++i)
      {
        PointT const curr = m_points[i];

        // Borders often have same points with ways
        if (equalF.EqualPoints(m_points[i], pt))
          return true;

        distance.SetBounds(prev, curr);
        if (distance(pt) < squareDelta)
          return true;

        prev = curr;
      }

      return false; // Point lies outside the border.
    }

    bool AtBorder(PointT const & pt, double const delta) const
    {
      return AtBorder(pt, delta, typename TraitsT::EqualType());
    }

  private:
    void CalcLimitRect()
    {
      m_rect.MakeEmpty();
      for (size_t i = 0; i < m_points.size(); ++i)
        m_rect.Add(m_points[i]);
    }

    ContainerT m_points;
    m2::Rect<CoordT> m_rect;

    template <class T> friend string DebugPrint(Region<T> const &);
  };

  template <class PointT>
  void swap(Region<PointT> & r1, Region<PointT> & r2)
  {
    r1.Swap(r2);
  }

  template <class TArchive, class PointT>
  TArchive & operator >> (TArchive & ar, Region<PointT> & region)
  {
    ar >> region.m_rect;
    ar >> region.m_points;
    return ar;
  }

  template <class TArchive, class PointT>
  TArchive & operator << (TArchive & ar, Region<PointT> const & region)
  {
    ar << region.m_rect;
    ar << region.m_points;
    return ar;
  }

  template <class PointT>
  inline string DebugPrint(Region<PointT> const & r)
  {
    return (DebugPrint(r.m_rect) + ::DebugPrint(r.m_points));
  }

  typedef Region<m2::PointD> RegionD;
  typedef Region<m2::PointI> RegionI;
  typedef Region<m2::PointU> RegionU;
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2017, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file LinkedObject.hxx
 *
 * Helper class that allows all instances of a specific class to be maintained
 * in a linked list.
 *
 * @author Balazs Racz
 * @date 9 Sep 2017
 */

#ifndef _UTILS_LINKEDOBJECT_HXX_
#define _UTILS_LINKEDOBJECT_HXX_

#include "utils/Atomic.hxx"

/// Using this class as a base class will cause the given class to have all its
/// instances linked up in a list. The cost is 4 bytes per object, and some CPU
/// cost in the destructor (to walk the list).
template <class T> class LinkedObject
{
public:
    /// @return the subclass pointer next on the list (or nullptr if we're at
    /// the end of the list).
    T *link_next()
    {
        return static_cast<T *>(link_);
    }

protected:
    /// @return the current subclass pointer.
    T *link_this()
    {
        return static_cast<T *>(this);
    }

    /// Constructor. Puts *this on the linked list.
    LinkedObject()
    {
        AtomicHolder h(&headMu_);
        link_ = head_;
        head_ = link_this();
    }

    /// Constructor. Removes *this from the linked list.
    ~LinkedObject()
    {
        AtomicHolder h(&headMu_);
        T **p = &head_;
        while (*p && *p != this)
        {
            p = &((*p)->link_);
        }
        if (*p == this)
        {
            *p = this->link_;
        }
        else
        {
            HASSERT(0);
        }
    }

    /// Linked list pointer.
    T *link_;
    /// Beginning of the list.
    static T *head_;
    /// Locks the list for modification (at any entry!).
    static Atomic headMu_;
};

// static
template <class T> T *LinkedObject<T>::head_{nullptr};
template <class T> Atomic LinkedObject<T>::headMu_;

#endif // _UTILS_LINKEDOBJECT_HXX_
<commit_msg>Disambiguates reference in case one class has multiple LinkedObject bases.<commit_after>/** \copyright
 * Copyright (c) 2017, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file LinkedObject.hxx
 *
 * Helper class that allows all instances of a specific class to be maintained
 * in a linked list.
 *
 * @author Balazs Racz
 * @date 9 Sep 2017
 */

#ifndef _UTILS_LINKEDOBJECT_HXX_
#define _UTILS_LINKEDOBJECT_HXX_

#include "utils/Atomic.hxx"

/// Using this class as a base class will cause the given class to have all its
/// instances linked up in a list. The cost is 4 bytes per object, and some CPU
/// cost in the destructor (to walk the list).
template <class T> class LinkedObject
{
public:
    /// @return the subclass pointer next on the list (or nullptr if we're at
    /// the end of the list).
    T *link_next()
    {
        return static_cast<T *>(link_);
    }

protected:
    /// @return the current subclass pointer.
    T *link_this()
    {
        return static_cast<T *>(this);
    }

    /// Constructor. Puts *this on the linked list.
    LinkedObject()
    {
        AtomicHolder h(&headMu_);
        link_ = head_;
        head_ = link_this();
    }

    /// Constructor. Removes *this from the linked list.
    ~LinkedObject()
    {
        AtomicHolder h(&headMu_);
        T **p = &head_;
        while (*p && *p != this)
        {
            p = &((*p)->LinkedObject<T>::link_);
        }
        if (*p == this)
        {
            *p = this->link_;
        }
        else
        {
            HASSERT(0);
        }
    }

    /// Linked list pointer.
    T *link_;
    /// Beginning of the list.
    static T *head_;
    /// Locks the list for modification (at any entry!).
    static Atomic headMu_;
};

// static
template <class T> T *LinkedObject<T>::head_{nullptr};
template <class T> Atomic LinkedObject<T>::headMu_;

#endif // _UTILS_LINKEDOBJECT_HXX_
<|endoftext|>


<commit_before>/** @copyright
 * Copyright (c) 2013, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @file SocketClient.hxx
 *
 * Connects to a remote TCP socket.
 *
 * @author Stuart Baker
 * @date 11 March 2017
 */

#ifndef _UTILS_SOCKET_CLIENT_HXX_
#define _UTILS_SOCKET_CLIENT_HXX_

#include <functional>
#include <netdb.h>
#include <arpa/inet.h>

#include "executor/StateFlow.hxx"
#include "executor/Timer.hxx"

class SocketClient : public StateFlowBase, public OSThread , public OSSem
{
public:
    /** Constructor.
     * @param service service that the StateFlowBase will be bound to.
     * @param mdns_name service name to connect to, nullptr to force use
     *                  hostsname and port
     * @param host host to connect to if mdns_name resolution fails,
     *             nullptr to force use mDNS
     * @param port port number to connect to if mdns_name resolution fails
     * @param callback callback method to invoke when a client connection is
     *                 made successfully.  It is the responsibility of the
     *                 callee to register the notifiable (on close) if this
     *                 client shall reattempt the connection if the socket
     *                 is ever closed.
     * @param retry_seconds time in seconds that the client shall wait to retry
     *                      connecting on error.
     */
    SocketClient(Service *service, const char *mdns, const char * host,
                 int port,
                 std::function<void(int, char*, int, Notifiable*)>callback,
                 unsigned retry_seconds = 5)
        : StateFlowBase(service)
        , OSThread()
        , OSSem()
        , mdns_(mdns)
        , host_(host)
        , port_(port)
        , callback_(callback)
        , retrySeconds_(retry_seconds)
        , selectHelper_(this)
        , timer_(this)
        , fd_(-1)
        , addr_(nullptr)
    {
        HASSERT(mdns_ || (host_ && port_));
        start_flow(STATE(spawn_thread));
    }

private:
    /** thread that will handle the blocking address resolution.
     * @return should never return
     */
    virtual void *entry() override
    {
        int ai_ret;
        struct addrinfo hints;

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = 0;
        hints.ai_protocol = IPPROTO_TCP;

        for ( ; /* forever */ ; )
        {
            OSSem::wait();
            for ( ; /* forever */ ; )
            {
                if (mdns_)
                {
                    /* mdns address resolution */
                    ai_ret = getaddrinfo(nullptr, mdns_, &hints, &addr_);
                }
                else
                {
                    char port_str[30];
                    integer_to_buffer(port_, port_str);
                    ai_ret = getaddrinfo(host_, port_str, &hints, &addr_);
                }
                if (ai_ret == 0 && addr_)
                {
                    notify();
                    break;
                }
                if (addr_)
                {
                    freeaddrinfo(addr_);
                }
                sleep(retrySeconds_);
            }
        }

        return nullptr;
    }

    /** Entry point into the state flow.
     * @return next state is find_host()
     */
    Action spawn_thread()
    {
        start("socket_client", 0, 1024);
        return call_immediately(STATE(find_host));
    }

    /** Kick off host name resolution.
     * @return next state is do_connect() upon successful host name resolution
     */
    Action find_host()
    {
        OSSem::post();
        return wait_and_call(STATE(do_connect));
    }

    /** Start the connection attempt.
     * @return next stat is connect_active() on success or connect in progress,
     *         else find_host() after timeout on error
     */
    Action do_connect()
    {
        fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (fd_ < 0)
        {
            /* no available socket */
            return sleep_and_call(&timer_, SEC_TO_NSEC(retrySeconds_),
                                  STATE(find_host));
        }

        /* set to non-blocking */
        ::fcntl(fd_, F_SETFL, O_NONBLOCK);

        int ret = connect(fd_, addr_->ai_addr, addr_->ai_addrlen);
        if (ret == 0)
        {
            /* connect successful */
            return call_immediately(STATE(connect_active));
        }
        else if (ret < 0 && errno == EINPROGRESS)
        {
            /* connection pending */
            return connect_and_call(&selectHelper_, fd_, STATE(connect_active));
        }
        else
        {
            /* connect failed */
            close(fd_);
            return sleep_and_call(&timer_, SEC_TO_NSEC(retrySeconds_),
                                  STATE(find_host));
        }
    }

    /** Start the connection attempt
     * @return next state is find_host() after a timeout upon error, or upon
               success, after the connection has been broken
     */
    Action connect_active()
    {
        /// @todo small delay seems to be required for CC32xx.  Not sure why.
        usleep(10000);
        int ret = connect(fd_, addr_->ai_addr, addr_->ai_addrlen);
        freeaddrinfo(addr_);
        if (ret < 0)
        {
            if (errno != EISCONN)
            {
                /* connection failed or timed out */
                close(fd_);
                return sleep_and_call(&timer_, SEC_TO_NSEC(retrySeconds_),
                                      STATE(find_host));
            }
        }

        /* connect successful */
        callback_(fd_, nullptr, -1, this);
        return wait_and_call(STATE(find_host));
    }

    /** mDNS service name */
    const char *mdns_;

    /** hostname */
    const char *host_;

    /** port number */
    int port_;

    /** callback to call on connection success */
    std::function<void(int, char *, int, Notifiable*)> callback_;

    /** number of seconds between retries */
    unsigned retrySeconds_;

    /** helper for non-blocking connect */
    StateFlowSelectHelper selectHelper_;

    /** retry timer */
    StateFlowTimer timer_;

    /** socket descriptor */
    int fd_;

    /** address info metadata */
    struct addrinfo *addr_;

    DISALLOW_COPY_AND_ASSIGN(SocketClient);
};

#endif /* _UTILS_SOCKET_CLIENT_HXX */

<commit_msg>review comments and logic fix.<commit_after>/** @copyright
 * Copyright (c) 2013, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @file SocketClient.hxx
 *
 * Connects to a remote TCP socket.
 *
 * @author Stuart Baker
 * @date 11 March 2017
 */

#ifndef _UTILS_SOCKET_CLIENT_HXX_
#define _UTILS_SOCKET_CLIENT_HXX_

#include <functional>
#include <netdb.h>
#include <arpa/inet.h>

#include "executor/StateFlow.hxx"
#include "executor/Timer.hxx"

class SocketClient : public StateFlowBase, private OSThread
{
public:
    /** Constructor.
     * @param service service that the StateFlowBase will be bound to.
     * @param mdns service name to connect to, nullptr to force use
     *                  hostname and port
     * @param host host to connect to if mdns_name resolution fails,
     *             nullptr to force use mDNS
     * @param port port number to connect to if mdns_name resolution fails
     * @param callback callback method to invoke when a client connection is
     *                 made successfully.  It is the responsibility of the
     *                 callee to register the notifiable (on close) if this
     *                 client shall reattempt the connection if the socket
     *                 is ever closed.
     *                 - First param is the file descriptor of the resulting
     *                   socket
     *                 - Second param is the struct addrinfo for the connected
     *                   peer
     *                 - Third param is a pointer to "this" class to notify
     *                   on exit that the socket has been torn down.
     * @param retry_seconds time in seconds that the client shall wait to retry
     *                      connecting on error.
     */
    SocketClient(Service *service, const char *mdns, const char * host,
                 int port,
                 std::function<void(int, struct addrinfo *, Notifiable*)>callback,
                 unsigned retry_seconds = 5)
        : StateFlowBase(service)
        , OSThread()
        , mdns_(mdns)
        , host_(host)
        , port_(port)
        , callback_(callback)
        , retrySeconds_(retry_seconds)
        , selectHelper_(this)
        , timer_(this)
        , fd_(-1)
        , addr_(nullptr)
        , sem_()
    {
        HASSERT(mdns_ || (host_ && port_));
        start_flow(STATE(spawn_thread));
    }

    /** Destructor.
     */
    ~SocketClient()
    {
        if (addr_)
        {
            freeaddrinfo(addr_);
        }
    }

private:
    /** thread that will handle the blocking address resolution.
     * @return should never return
     */
    void *entry() override
    {
        int ai_ret = -1;
        struct addrinfo hints;

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = 0;
        hints.ai_protocol = IPPROTO_TCP;

        sem_.wait();

        for ( ; /* forever */ ; )
        {
            if (mdns_)
            {
                /* mdns address resolution */
                ai_ret = getaddrinfo(nullptr, mdns_, &hints, &addr_);
            }
            if (ai_ret != 0 || addr_ == nullptr)
            {
                char port_str[30];
                integer_to_buffer(port_, port_str);
                ai_ret = getaddrinfo(host_, port_str, &hints, &addr_);
            }
            if (ai_ret == 0 && addr_)
            {
                notify();
                sem_.wait();
            }
            if (addr_)
            {
                freeaddrinfo(addr_);
            }
            sleep(retrySeconds_);
        }

        return nullptr;
    }

    /** Entry point into the state flow.
     * @return next state is find_host()
     */
    Action spawn_thread()
    {
        start("socket_client", 0, 2048);
        return call_immediately(STATE(find_host));
    }

    /** Kick off host name resolution.
     * @return next state is do_connect() upon successful host name resolution
     */
    Action find_host()
    {
        sem_.post();
        return wait_and_call(STATE(do_connect));
    }

    /** Start the connection attempt.
     * @return next stat is connect_active() on success or connect in progress,
     *         else find_host() after timeout on error
     */
    Action do_connect()
    {
        fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (fd_ < 0)
        {
            /* no available socket */
            return sleep_and_call(&timer_, SEC_TO_NSEC(retrySeconds_),
                                  STATE(find_host));
        }

        /* set to non-blocking */
        ::fcntl(fd_, F_SETFL, O_NONBLOCK);

        int ret = connect(fd_, addr_->ai_addr, addr_->ai_addrlen);
        if (ret == 0)
        {
            /* connect successful */
            return call_immediately(STATE(connect_active));
        }
        else if (ret < 0 && errno == EINPROGRESS)
        {
            /* connection pending */
            return connect_and_call(&selectHelper_, fd_, STATE(connect_active));
        }
        else
        {
            /* connect failed */
            close(fd_);
            return sleep_and_call(&timer_, SEC_TO_NSEC(retrySeconds_),
                                  STATE(find_host));
        }
    }

    /** Change in connect state
     * @return next state is connect_active_delayed() after a timeout
     */
    Action connect_active()
    {
#if defined (__FreeRTOS__)
        /// @todo small delay seems to be required for CC32xx.  Not sure why.
        return sleep_and_call(&timer_, MSEC_TO_NSEC(100),
                              STATE(connect_active_delayed));
#else
        return call_immediately(STATE(connect_active_delayed));
#endif
    }

    /** complete the connection attempt
     * @return next state is find_host() after a timeout upon error, or upon
     *         success, after the connection has been broken
     */
    Action connect_active_delayed()
    {
        int ret = connect(fd_, addr_->ai_addr, addr_->ai_addrlen);
        if (ret < 0)
        {
            if (errno != EISCONN)
            {
                /* connection failed or timed out */
                close(fd_);
                return sleep_and_call(&timer_, SEC_TO_NSEC(retrySeconds_),
                                      STATE(find_host));
            }
        }

        /* connect successful */
        callback_(fd_, addr_, this);
        return wait_and_call(STATE(find_host));
    }

    /** mDNS service name */
    const char *mdns_;

    /** hostname */
    const char *host_;

    /** port number */
    int port_;

    /** callback to call on connection success */
    std::function<void(int, struct addrinfo *, Notifiable*)> callback_;

    /** number of seconds between retries */
    unsigned retrySeconds_;

    /** helper for non-blocking connect */
    StateFlowSelectHelper selectHelper_;

    /** retry timer */
    StateFlowTimer timer_;

    /** socket descriptor */
    int fd_;

    /** address info metadata */
    struct addrinfo *addr_;

    /** Semaphore for synchronizing with the helper thread */
    OSSem sem_;

    DISALLOW_COPY_AND_ASSIGN(SocketClient);
};

#endif /* _UTILS_SOCKET_CLIENT_HXX */

<|endoftext|>


<commit_before>#include "Writer.h"

#include <map>
#include <set>
#include <string>
#include <vector>

#include "../../Serialise/Serialise.h"
#include "../../Utils/Utils.h"
#include "../../llvmUtils/llvmUtils.h"
#include "Config.h"

namespace dale {
namespace Module {
Writer::Writer(
    std::string module_name, dale::Context *ctx, llvm::Module *mod,
    PassManager *pm, std::set<std::string> *included_once_tags,
    std::map<std::string, std::vector<std::string> *> *included_modules,
    bool cto) {
    this->module_name = module_name;
    this->ctx = ctx;
    this->mod = mod;
    this->pm = pm;
    this->included_once_tags = included_once_tags;
    this->included_modules = included_modules;
    this->cto = cto;

    this->module_prefix.append(module_name);
}

Writer::~Writer() {}

bool Writer::writeBitcode(const char *suffix) {
    std::string bc_path(module_prefix);
    bc_path.append(suffix).append(".bc");

    FILE *bc = fopen(bc_path.c_str(), "w");
    if (!bc) {
        error("unable to open %s for writing", bc_path.c_str(), true);
    }

    llvm::raw_fd_ostream bc_out(fileno(bc), false);

#if D_LLVM_VERSION_ORD <= 35
    pm->run(*mod);
    llvm::WriteBitcodeToFile(mod, bc_out);
#else
    std::unique_ptr<llvm::Module> module_ptr(llvm::CloneModule(mod));

    pm->run(*module_ptr);
    llvm::WriteBitcodeToFile(module_ptr.get(), bc_out);
#endif

    bc_out.flush();
    int res = fflush(bc);
    if (res != 0) {
        error("unable to flush %s", bc_path.c_str(), true);
    }
    res = fclose(bc);
    if (res != 0) {
        error("unable to close %s", bc_path.c_str(), true);
    }

    return true;
}

bool Writer::writeSharedObject(const char *suffix) {
    std::string asm_path(module_prefix);
    asm_path.append(suffix);

    std::string bc_path(asm_path);
    bc_path.append(".bc");

    asm_path.append(".s");

    std::string cmd;
    cmd.append(LLVM_BIN_DIR "/llc -relocation-model=pic -filetype=asm ")
        .append(bc_path)
        .append(" -o ")
        .append(asm_path);

    int res = system(cmd.c_str());
    assert(!res && "unable to assemble bitcode");
    _unused(res);

    std::string lib_path(module_prefix);
    lib_path.append(suffix);
    lib_path.append(".so");

    cmd.clear();
    cmd.append(DALE_CC " -shared ");
    if (!strcmp(SYSTEM_NAME, "Darwin")) {
        cmd.append(
            " -undefined dynamic_lookup"
            " -install_name @rpath/");
        cmd.append(lib_path);
        cmd.append(" ");
    }
    cmd.append(asm_path).append(" -o ").append(lib_path);

    res = system(cmd.c_str());
    assert(!res && "unable to make library");

    res = remove(asm_path.c_str());
    assert(!res && "unable to remove temporary assembly file");

    return true;
}

bool Writer::writeContext() {
    ctx->removeDeserialised();

    std::string ctx_module_path(module_prefix);
    module_prefix.append(".dtm");

    FILE *mod_data = fopen(module_prefix.c_str(), "w");
    if (!mod_data) {
        error("unable to open %s for writing", module_prefix.c_str(),
              true);
    }

    assert(mod_data && "cannot create module file");
    serialise(mod_data, ctx);
    serialise(mod_data, included_once_tags);
    serialise(mod_data, included_modules);
    serialise(mod_data, cto);
    serialise(mod_data, &dale_typemap);

    int res = fflush(mod_data);
    if (res != 0) {
        error("unable to flush %s", module_prefix.c_str(), true);
    }
    res = fclose(mod_data);
    if (res != 0) {
        error("unable to close %s", module_prefix.c_str(), true);
    }

    return true;
}

bool Writer::run() {
    writeBitcode("");
    writeSharedObject("");
    ctx->eraseLLVMMacrosAndCTOFunctions();
    writeBitcode("-nomacros");
    writeSharedObject("-nomacros");
    writeContext();
    return true;
}
}
}
<commit_msg>[master] Fix for earlier versions of LLVM (2)<commit_after>#include "Writer.h"

#include <map>
#include <set>
#include <string>
#include <vector>

#include "../../Serialise/Serialise.h"
#include "../../Utils/Utils.h"
#include "../../llvmUtils/llvmUtils.h"
#include "Config.h"

namespace dale {
namespace Module {
Writer::Writer(
    std::string module_name, dale::Context *ctx, llvm::Module *mod,
    PassManager *pm, std::set<std::string> *included_once_tags,
    std::map<std::string, std::vector<std::string> *> *included_modules,
    bool cto) {
    this->module_name = module_name;
    this->ctx = ctx;
    this->mod = mod;
    this->pm = pm;
    this->included_once_tags = included_once_tags;
    this->included_modules = included_modules;
    this->cto = cto;

    this->module_prefix.append(module_name);
}

Writer::~Writer() {}

bool Writer::writeBitcode(const char *suffix) {
    std::string bc_path(module_prefix);
    bc_path.append(suffix).append(".bc");

    FILE *bc = fopen(bc_path.c_str(), "w");
    if (!bc) {
        error("unable to open %s for writing", bc_path.c_str(), true);
    }

    llvm::raw_fd_ostream bc_out(fileno(bc), false);

#if D_LLVM_VERSION_ORD <= 35
    pm->run(*mod);
    llvm::WriteBitcodeToFile(mod, bc_out);
#else
    std::unique_ptr<llvm::Module> module_ptr(llvm::CloneModule(mod));

    pm->run(*module_ptr);
    llvm::WriteBitcodeToFile(module_ptr.get(), bc_out);
#endif

    bc_out.flush();
    int res = fflush(bc);
    if (res != 0) {
        error("unable to flush %s", bc_path.c_str(), true);
    }
    res = fclose(bc);
    if (res != 0) {
        error("unable to close %s", bc_path.c_str(), true);
    }

    return true;
}

bool Writer::writeSharedObject(const char *suffix) {
    std::string asm_path(module_prefix);
    asm_path.append(suffix);

    std::string bc_path(asm_path);
    bc_path.append(".bc");

    asm_path.append(".s");

    std::string cmd;
    cmd.append(LLVM_BIN_DIR "/llc -relocation-model=pic -filetype=asm ")
        .append(bc_path)
        .append(" -o ")
        .append(asm_path);

    int res = system(cmd.c_str());
    assert(!res && "unable to assemble bitcode");
    _unused(res);

    std::string lib_path(module_prefix);
    lib_path.append(suffix);
    lib_path.append(".so");

    cmd.clear();
    cmd.append(DALE_CC " -shared ");
    if (!strcmp(SYSTEM_NAME, "Darwin")) {
        cmd.append(
            " -undefined dynamic_lookup"
            " -install_name @rpath/");
        cmd.append(lib_path);
        cmd.append(" ");
    }
    cmd.append(asm_path).append(" -o ").append(lib_path);

    res = system(cmd.c_str());
    assert(!res && "unable to make library");

    res = remove(asm_path.c_str());
    assert(!res && "unable to remove temporary assembly file");

    return true;
}

bool Writer::writeContext() {
    ctx->removeDeserialised();

    std::string ctx_module_path(module_prefix);
    module_prefix.append(".dtm");

    FILE *mod_data = fopen(module_prefix.c_str(), "w");
    if (!mod_data) {
        error("unable to open %s for writing", module_prefix.c_str(),
              true);
    }

    assert(mod_data && "cannot create module file");
    serialise(mod_data, ctx);
    serialise(mod_data, included_once_tags);
    serialise(mod_data, included_modules);
    serialise(mod_data, cto);
    serialise(mod_data, &dale_typemap);

    int res = fflush(mod_data);
    if (res != 0) {
        error("unable to flush %s", module_prefix.c_str(), true);
    }
    res = fclose(mod_data);
    if (res != 0) {
        error("unable to close %s", module_prefix.c_str(), true);
    }

    return true;
}

bool Writer::run() {
    writeBitcode("");
    writeSharedObject("");
#if D_LLVM_VERSION_ORD <= 35
    ctx->regetPointers(mod);
#endif
    ctx->eraseLLVMMacrosAndCTOFunctions();
    writeBitcode("-nomacros");
    writeSharedObject("-nomacros");
    writeContext();
    return true;
}
}
}
<|endoftext|>


<commit_before>#include "MqttClient.hpp"
#include "Timer.hpp"
#include <iostream>

#define MQTT_PROTO_MAJOR 3
#define MQTT_PROTO_MINOR 1
#define MQTT_PROTOCOL_VERSION "MQTT/3.1"

#define PROTOCOL_MAGIC "MQIsdp"

#define FLAG_CLEANSESS(F, C)(F | ((C) << 1))
#define FLAG_WILL(F, W)(F | ((W) << 2))
#define FLAG_WILLQOS(F, Q)(F | ((Q) << 3))
#define FLAG_WILLRETAIN(F, R) (F | ((R) << 5))
#define FLAG_PASSWD(F, P)(F | ((P) << 6))
#define FLAG_USERNAME(F, U)(F | ((U) << 7))

using namespace Kite;

class Frame
{
public:
    enum MessageType
    {
        //0,  //Reserved
        CONNECT     = 1,  //Client request to connect to Server
        CONNACK     = 2,  //Connect Acknowledgment
        PUBLISH     = 3,  //Publish message
        PUBACK      = 4,  //Publish Acknowledgment
        PUBREC      = 5,  //Publish Received (assured delivery part 1)
        PUBREL      = 6,  //Publish Release (assured delivery part 2)
        PUBCOMP     = 7,  //Publish Complete (assured delivery part 3)
        SUBSCRIBE   = 8,  //Client Subscribe request
        SUBACK      = 9,  //Subscribe Acknowledgment
        UNSUBSCRIBE = 10, //Client Unsubscribe request
        UNSUBACK11  = 11, //Unsubscribe Acknowledgment
        PINGREQ     = 12, //PING Request
        PINGRESP    = 13, //PING Response
        DISCONNECT  = 14, //Client is Disconnecting
        //15  // Reserved
    };

    Frame(MessageType type = MessageType(0), int qos = 0, bool retain = false, bool dup = false)
        : expectedSize(0)
        , type(type)
        , qos(qos)
        , retain(retain)
        , dup(dup)
    { }
    int expectedSize;
    MessageType type;
    uint8_t qos;
    bool retain;
    bool dup;
    //TODO std::string is an inefficient buffer
    std::string data;

    Frame(uint8_t header, const std::string &data);

    int          size() const { return data.size(); }
    uint16_t     readInt();
    uint8_t      readByte();
    std::string readString();

    void writeInt(uint16_t i);
    void writeByte(uint8_t c);
    void writeString (const std::string& string);
    void writeRawData(const std::string& data);

    void parcel(Kite::IO *io) const;
};

///////////////////////////////////////////////////////////////////////////////

class Kite::MqttClientPrivate : public Kite::Timer
{
public:
    MqttClientPrivate(std::weak_ptr<Kite::EventLoop> ev)
        : Kite::Timer(ev)
    {
        KITE_TIMER_DEBUG_NAME(this, "MqttClientPrivate");
    }

    Kite::MqttClient *p;
    std::string clientId;
    std::string username;
    std::string password;

    std::string willTopic;
    std::string willMessage;
    int willQos;
    bool willRetain;

    bool cleanSess;

    int keepAlive;
    std::vector<char> buffer;
    int expectedSize;
    uint8_t nextHeader;
    int readPayloadSize()
    {
        int8_t byte;
        int len = 0;
        int mul = 1;
        do {
            //TODO we hope we got the whole header in one tcp package
            //otherwise we crash
            byte = buffer[0];
            buffer.erase(buffer.begin());
            len += (byte & 127) * mul;
            mul *= 128  ;
        } while ((byte & 128) != 0);
        return len;
    }
    uint16_t nextMessageId;

    void onCONNACK (Frame &frame);
    void onPUBACK  (Frame &frame);
    void onSUBACK  (Frame &frame);
    void onPUBLISH (Frame &frame);

    bool isConnected;

    virtual bool onExpired() override {
        if (!isConnected)
            return true;

        Frame frame(Frame::PINGREQ, 0);
        frame.parcel(p);
        p->flush();

        return true;
    }

};

///////////////////////////////////////////////////////////////////////////////

MqttClient::MqttClient(std::weak_ptr<Kite::EventLoop> ev)
    : Kite::SecureSocket(ev)
    , p(new MqttClientPrivate(ev))
{
    p->p = this;
    p->keepAlive = 3;
    p->clientId  = "kitemqtt";
    p->expectedSize = 0;
    p->nextMessageId = 1;
    p->isConnected = false;
    p->cleanSess = false;
}

MqttClient::~MqttClient()
{
    delete p;
}

void MqttClient::setCleanSession   (bool clean)
{
    p->cleanSess = clean;
}

void MqttClient::setClientId(const std::string &v)
{
    p->clientId = v;
}

void MqttClient::setKeepAlive(int v)
{
    p->keepAlive = v;
    p->reset(p->keepAlive * 1000);
}

void MqttClient::setWill(const std::string &topic, const std::string &message, int qos, bool retain)
{
    p->willTopic   = topic;
    p->willMessage = message;
    p->willQos     = qos;
    p->willRetain  = retain;
}

void MqttClient::onActivated(int)
{
    int osize = p->buffer.size();
    p->buffer.resize(osize + 201);
    int r = read(p->buffer.data() + osize, 200);

    if (r < 1) {
        //AGAIN
        //TODO:
        //this happens when the fd gets ready but ssl frames arent complete
        //don't re-buffer to remove the problem.
        //it's ok if the higher layer gets an EAGAIN,
        //but we need an abstract concept
        //at least enum or something, not ints
        if (r == -1) {
            p->buffer.resize(osize);
            return;
        }

        //TODO
        fprintf(stderr, "ssl read error: %d\n", r);
        ev()->exit(0);
        return;
    }
    p->buffer.resize(osize + r);

    while (p->buffer.size() > 2) {
        if (p->expectedSize == 0) {
            p->nextHeader = p->buffer[0];
            p->buffer.erase(p->buffer.begin());
            p->expectedSize = p->readPayloadSize();
        }

        std::string data;
        if (p->expectedSize != 0) {
            if (p->buffer.size() < p->expectedSize)
                break;

            data = std::string(p->buffer.begin(), p->buffer.begin() + p->expectedSize);
            p->buffer.erase(p->buffer.begin(), p->buffer.begin() + p->expectedSize);
            p->expectedSize = 0;
        }

        Frame frame(p->nextHeader, data);
        //std::cerr << "msg type" << frame.type << std::endl;
        switch (frame.type) {
            case Frame::CONNACK:
                p->onCONNACK(frame);
                break;
            case Frame::PUBACK:
                p->onPUBACK(frame);
                break;
            case Frame::SUBACK:
                p->onSUBACK(frame);
                break;
            case Frame::PUBLISH:
                p->onPUBLISH(frame);
                break;
            case Frame::PINGRESP:
                break;
            default:
                std::cerr  << "unhandled message type " << frame.type << std::endl;
        }
    }

    //have more?
    //TODO wont get activated again. this is not properly abstracted away in SecureSocket
    if (r >= 200) {
        onActivated(0);
    }
}

void MqttClient::onConnected() {
    fprintf(stderr, "connected!\n");

    Frame frame(Frame::CONNECT, 0);
    frame.writeString(PROTOCOL_MAGIC);
    frame.writeByte(MQTT_PROTO_MAJOR);

    uint8_t flags = 0;
    //flags
    flags = FLAG_CLEANSESS(flags, p->cleanSess? 1 : 0 );

    if (!p->willTopic.empty()) {
        flags = FLAG_WILL(flags, 1);
        flags = FLAG_WILLQOS(flags, p->willQos);
        flags = FLAG_WILLRETAIN(flags, p->willRetain ? 1 : 0);
    }
    if(!p->username.empty()) {
        flags = FLAG_USERNAME(flags, 1);
    }
    if (!p->password.empty()) {
        flags = FLAG_PASSWD(flags, 1);
    }
    frame.writeByte(flags);
    frame.writeInt(p->keepAlive * 2);
    frame.writeString(p->clientId);
    if (!p->willTopic.empty()) {
        frame.writeString(p->willTopic);
        frame.writeString(p->willMessage);
    }
    if (!p->username.empty()) {
        frame.writeString(p->username);
    }
    if (!p->password.empty()) {
        frame.writeString(p->password);
    }

    frame.parcel(this);
    flush();
    p->isConnected = true;
    p->reset(p->keepAlive * 1000);
}

void MqttClient::publish(const std::string &topic, const std::string &message, int qos, bool retain)
{
    Frame frame(Frame::PUBLISH, qos);
    frame.writeString(topic);
    if (qos > 0) {
        if (++(p->nextMessageId) == 0) p->nextMessageId = 1;
        frame.writeInt(p->nextMessageId);
    }
    frame.writeRawData(message);
    frame.parcel(this);
    flush();
}
void MqttClient::subscribe(const std::string &topic, int qos)
{
    Frame frame(Frame::SUBSCRIBE, 1);
    if (++(p->nextMessageId) == 0) p->nextMessageId = 1;
    frame.writeInt(p->nextMessageId);
    frame.writeString(topic);
    frame.writeByte(qos);
    frame.parcel(this);
    flush();
}

void MqttClientPrivate::onCONNACK (Frame &frame)
{
    frame.readByte();
    int status = frame.readByte();
    std::cerr << "connection status: " << status << std::endl;
    if (status != 0) {
        p->onProtocolError(MqttClient::ProtocolError(status));
        p->disconnect();
        return;
    }
    p->onMqttConnected();
}

void MqttClientPrivate::onPUBACK  (Frame &frame)  {
    int id = frame.readInt();
    std::cerr<< "ackd publish " << id << std::endl;
}
void MqttClientPrivate::onSUBACK  (Frame &frame)  {
    int id = frame.readInt();
    std::cerr<< "ackd sub " << id << std::endl;
}
void MqttClientPrivate::onPUBLISH (Frame &frame)  {
    std::string topic = frame.readString();
    uint16_t mid = 0;
    if (frame.qos > 0) {
        mid = frame.readInt();
    }
    std::string m  = frame.data;
    p->onPublished(topic, m);

    if (frame.qos == 1) {
        Frame frame(Frame::PUBACK, 0);
        frame.writeInt(mid);
        frame.parcel(p);
        p->flush();
    } else if (frame.qos == 2) {
        std::cerr << "QOS2 not implemented\n";
        //TODO
    }
}

///////////////////////////////////////////////////////////////////////////////

#define LSB(A) (uint8_t)(A & 0x00FF)
#define MSB(A) (uint8_t)((A & 0xFF00) >> 8)

Frame::Frame(uint8_t header, const std::string &data)
    : data(data)
{
    type   = Frame::MessageType(header >> 4);
    dup    = header & (1 << 3);
    qos    = (header >> 1) & 3;
    retain = header & 1;
}

uint8_t Frame::readByte()
{
    uint8_t c = data[0];
    data.erase(0, 1);
    return c;
}

uint16_t Frame::readInt()
{
    uint8_t msb = data[0];
    uint8_t lsb = data[1];
    data.erase(0, 2);
    //TODO: this doesnt look right. will this convert to a wider type before shift?
    return (msb << 8) | lsb;
}

std::string Frame::readString()
{
    int len = readInt();
    std::string s = std::string(data.substr(0, len));
    data.erase(0, len);
    return s;
}

//TODO this is BS. just write the 16bit int in big endian
void Frame::writeInt(uint16_t i)
{
    data += MSB(i);
    data += LSB(i);
}

void Frame::writeString(const std::string &string)
{
    writeInt(string.size());
    data.append(string);
}

void Frame::writeByte(uint8_t c)
{
    data += char(c);
}

void Frame::writeRawData(const std::string &d)
{
    data.append(d);
}

void Frame::parcel(Kite::IO *io) const
{
    uint8_t header = type << 4;
    if (dup)
        header |= 1 << 3;
    header |= qos << 1;
    if (retain)
        header  |= 1;

    io->write((char*)&header, 1);

    if (data.size() == 0) {
        io->write("\0", 1);
        return;
    }

    //length
    std::vector<char> r;
    int length = data.size();
    uint8_t d;
    do {
        d = length % 128;
        length /= 128;
        if (length > 0) {
            d |= 0x80;
        }
        r.push_back(d);
    } while (length > 0);

    io->write(r.data(), r.size());
    io->write(data.c_str(), data.size());
    return;
}
<commit_msg>publish retain<commit_after>#include "MqttClient.hpp"
#include "Timer.hpp"
#include <iostream>

#define MQTT_PROTO_MAJOR 3
#define MQTT_PROTO_MINOR 1
#define MQTT_PROTOCOL_VERSION "MQTT/3.1"

#define PROTOCOL_MAGIC "MQIsdp"

#define FLAG_CLEANSESS(F, C)(F | ((C) << 1))
#define FLAG_WILL(F, W)(F | ((W) << 2))
#define FLAG_WILLQOS(F, Q)(F | ((Q) << 3))
#define FLAG_WILLRETAIN(F, R) (F | ((R) << 5))
#define FLAG_PASSWD(F, P)(F | ((P) << 6))
#define FLAG_USERNAME(F, U)(F | ((U) << 7))

using namespace Kite;

class Frame
{
public:
    enum MessageType
    {
        //0,  //Reserved
        CONNECT     = 1,  //Client request to connect to Server
        CONNACK     = 2,  //Connect Acknowledgment
        PUBLISH     = 3,  //Publish message
        PUBACK      = 4,  //Publish Acknowledgment
        PUBREC      = 5,  //Publish Received (assured delivery part 1)
        PUBREL      = 6,  //Publish Release (assured delivery part 2)
        PUBCOMP     = 7,  //Publish Complete (assured delivery part 3)
        SUBSCRIBE   = 8,  //Client Subscribe request
        SUBACK      = 9,  //Subscribe Acknowledgment
        UNSUBSCRIBE = 10, //Client Unsubscribe request
        UNSUBACK11  = 11, //Unsubscribe Acknowledgment
        PINGREQ     = 12, //PING Request
        PINGRESP    = 13, //PING Response
        DISCONNECT  = 14, //Client is Disconnecting
        //15  // Reserved
    };

    Frame(MessageType type = MessageType(0), int qos = 0, bool retain = false, bool dup = false)
        : expectedSize(0)
        , type(type)
        , qos(qos)
        , retain(retain)
        , dup(dup)
    { }
    int expectedSize;
    MessageType type;
    uint8_t qos;
    bool retain;
    bool dup;
    //TODO std::string is an inefficient buffer
    std::string data;

    Frame(uint8_t header, const std::string &data);

    int          size() const { return data.size(); }
    uint16_t     readInt();
    uint8_t      readByte();
    std::string readString();

    void writeInt(uint16_t i);
    void writeByte(uint8_t c);
    void writeString (const std::string& string);
    void writeRawData(const std::string& data);

    void parcel(Kite::IO *io) const;
};

///////////////////////////////////////////////////////////////////////////////

class Kite::MqttClientPrivate : public Kite::Timer
{
public:
    MqttClientPrivate(std::weak_ptr<Kite::EventLoop> ev)
        : Kite::Timer(ev)
    {
        KITE_TIMER_DEBUG_NAME(this, "MqttClientPrivate");
    }

    Kite::MqttClient *p;
    std::string clientId;
    std::string username;
    std::string password;

    std::string willTopic;
    std::string willMessage;
    int willQos;
    bool willRetain;

    bool cleanSess;

    int keepAlive;
    std::vector<char> buffer;
    int expectedSize;
    uint8_t nextHeader;
    int readPayloadSize()
    {
        int8_t byte;
        int len = 0;
        int mul = 1;
        do {
            //TODO we hope we got the whole header in one tcp package
            //otherwise we crash
            byte = buffer[0];
            buffer.erase(buffer.begin());
            len += (byte & 127) * mul;
            mul *= 128  ;
        } while ((byte & 128) != 0);
        return len;
    }
    uint16_t nextMessageId;

    void onCONNACK (Frame &frame);
    void onPUBACK  (Frame &frame);
    void onSUBACK  (Frame &frame);
    void onPUBLISH (Frame &frame);

    bool isConnected;

    virtual bool onExpired() override {
        if (!isConnected)
            return true;

        Frame frame(Frame::PINGREQ, 0);
        frame.parcel(p);
        p->flush();

        return true;
    }

};

///////////////////////////////////////////////////////////////////////////////

MqttClient::MqttClient(std::weak_ptr<Kite::EventLoop> ev)
    : Kite::SecureSocket(ev)
    , p(new MqttClientPrivate(ev))
{
    p->p = this;
    p->keepAlive = 3;
    p->clientId  = "kitemqtt";
    p->expectedSize = 0;
    p->nextMessageId = 1;
    p->isConnected = false;
    p->cleanSess = false;
}

MqttClient::~MqttClient()
{
    delete p;
}

void MqttClient::setCleanSession   (bool clean)
{
    p->cleanSess = clean;
}

void MqttClient::setClientId(const std::string &v)
{
    p->clientId = v;
}

void MqttClient::setKeepAlive(int v)
{
    p->keepAlive = v;
    p->reset(p->keepAlive * 1000);
}

void MqttClient::setWill(const std::string &topic, const std::string &message, int qos, bool retain)
{
    p->willTopic   = topic;
    p->willMessage = message;
    p->willQos     = qos;
    p->willRetain  = retain;
}

void MqttClient::onActivated(int)
{
    int osize = p->buffer.size();
    p->buffer.resize(osize + 201);
    int r = read(p->buffer.data() + osize, 200);

    if (r < 1) {
        //AGAIN
        //TODO:
        //this happens when the fd gets ready but ssl frames arent complete
        //don't re-buffer to remove the problem.
        //it's ok if the higher layer gets an EAGAIN,
        //but we need an abstract concept
        //at least enum or something, not ints
        if (r == -1) {
            p->buffer.resize(osize);
            return;
        }

        //TODO
        fprintf(stderr, "ssl read error: %d\n", r);
        ev()->exit(0);
        return;
    }
    p->buffer.resize(osize + r);

    while (p->buffer.size() > 2) {
        if (p->expectedSize == 0) {
            p->nextHeader = p->buffer[0];
            p->buffer.erase(p->buffer.begin());
            p->expectedSize = p->readPayloadSize();
        }

        std::string data;
        if (p->expectedSize != 0) {
            if (p->buffer.size() < p->expectedSize)
                break;

            data = std::string(p->buffer.begin(), p->buffer.begin() + p->expectedSize);
            p->buffer.erase(p->buffer.begin(), p->buffer.begin() + p->expectedSize);
            p->expectedSize = 0;
        }

        Frame frame(p->nextHeader, data);
        //std::cerr << "msg type" << frame.type << std::endl;
        switch (frame.type) {
            case Frame::CONNACK:
                p->onCONNACK(frame);
                break;
            case Frame::PUBACK:
                p->onPUBACK(frame);
                break;
            case Frame::SUBACK:
                p->onSUBACK(frame);
                break;
            case Frame::PUBLISH:
                p->onPUBLISH(frame);
                break;
            case Frame::PINGRESP:
                break;
            default:
                std::cerr  << "unhandled message type " << frame.type << std::endl;
        }
    }

    //have more?
    //TODO wont get activated again. this is not properly abstracted away in SecureSocket
    if (r >= 200) {
        onActivated(0);
    }
}

void MqttClient::onConnected() {
    fprintf(stderr, "connected!\n");

    Frame frame(Frame::CONNECT, 0);
    frame.writeString(PROTOCOL_MAGIC);
    frame.writeByte(MQTT_PROTO_MAJOR);

    uint8_t flags = 0;
    //flags
    flags = FLAG_CLEANSESS(flags, p->cleanSess? 1 : 0 );

    if (!p->willTopic.empty()) {
        flags = FLAG_WILL(flags, 1);
        flags = FLAG_WILLQOS(flags, p->willQos);
        flags = FLAG_WILLRETAIN(flags, p->willRetain ? 1 : 0);
    }
    if(!p->username.empty()) {
        flags = FLAG_USERNAME(flags, 1);
    }
    if (!p->password.empty()) {
        flags = FLAG_PASSWD(flags, 1);
    }
    frame.writeByte(flags);
    frame.writeInt(p->keepAlive * 2);
    frame.writeString(p->clientId);
    if (!p->willTopic.empty()) {
        frame.writeString(p->willTopic);
        frame.writeString(p->willMessage);
    }
    if (!p->username.empty()) {
        frame.writeString(p->username);
    }
    if (!p->password.empty()) {
        frame.writeString(p->password);
    }

    frame.parcel(this);
    flush();
    p->isConnected = true;
    p->reset(p->keepAlive * 1000);
}

void MqttClient::publish(const std::string &topic, const std::string &message, int qos, bool retain)
{
    Frame frame(Frame::PUBLISH, qos, retain);
    frame.writeString(topic);
    if (qos > 0) {
        if (++(p->nextMessageId) == 0) p->nextMessageId = 1;
        frame.writeInt(p->nextMessageId);
    }
    frame.writeRawData(message);
    frame.parcel(this);
    flush();
}
void MqttClient::subscribe(const std::string &topic, int qos)
{
    Frame frame(Frame::SUBSCRIBE, 1);
    if (++(p->nextMessageId) == 0) p->nextMessageId = 1;
    frame.writeInt(p->nextMessageId);
    frame.writeString(topic);
    frame.writeByte(qos);
    frame.parcel(this);
    flush();
}

void MqttClientPrivate::onCONNACK (Frame &frame)
{
    frame.readByte();
    int status = frame.readByte();
    std::cerr << "connection status: " << status << std::endl;
    if (status != 0) {
        p->onProtocolError(MqttClient::ProtocolError(status));
        p->disconnect();
        return;
    }
    p->onMqttConnected();
}

void MqttClientPrivate::onPUBACK  (Frame &frame)  {
    int id = frame.readInt();
    std::cerr<< "ackd publish " << id << std::endl;
}
void MqttClientPrivate::onSUBACK  (Frame &frame)  {
    int id = frame.readInt();
    std::cerr<< "ackd sub " << id << std::endl;
}
void MqttClientPrivate::onPUBLISH (Frame &frame)  {
    std::string topic = frame.readString();
    uint16_t mid = 0;
    if (frame.qos > 0) {
        mid = frame.readInt();
    }
    std::string m  = frame.data;
    p->onPublished(topic, m);

    if (frame.qos == 1) {
        Frame frame(Frame::PUBACK, 0);
        frame.writeInt(mid);
        frame.parcel(p);
        p->flush();
    } else if (frame.qos == 2) {
        std::cerr << "QOS2 not implemented\n";
        //TODO
    }
}

///////////////////////////////////////////////////////////////////////////////

#define LSB(A) (uint8_t)(A & 0x00FF)
#define MSB(A) (uint8_t)((A & 0xFF00) >> 8)

Frame::Frame(uint8_t header, const std::string &data)
    : data(data)
{
    type   = Frame::MessageType(header >> 4);
    dup    = header & (1 << 3);
    qos    = (header >> 1) & 3;
    retain = header & 1;
}

uint8_t Frame::readByte()
{
    uint8_t c = data[0];
    data.erase(0, 1);
    return c;
}

uint16_t Frame::readInt()
{
    uint8_t msb = data[0];
    uint8_t lsb = data[1];
    data.erase(0, 2);
    //TODO: this doesnt look right. will this convert to a wider type before shift?
    return (msb << 8) | lsb;
}

std::string Frame::readString()
{
    int len = readInt();
    std::string s = std::string(data.substr(0, len));
    data.erase(0, len);
    return s;
}

//TODO this is BS. just write the 16bit int in big endian
void Frame::writeInt(uint16_t i)
{
    data += MSB(i);
    data += LSB(i);
}

void Frame::writeString(const std::string &string)
{
    writeInt(string.size());
    data.append(string);
}

void Frame::writeByte(uint8_t c)
{
    data += char(c);
}

void Frame::writeRawData(const std::string &d)
{
    data.append(d);
}

void Frame::parcel(Kite::IO *io) const
{
    uint8_t header = type << 4;
    if (dup)
        header |= 1 << 3;
    header |= qos << 1;
    if (retain)
        header  |= 1;

    io->write((char*)&header, 1);

    if (data.size() == 0) {
        io->write("\0", 1);
        return;
    }

    //length
    std::vector<char> r;
    int length = data.size();
    uint8_t d;
    do {
        d = length % 128;
        length /= 128;
        if (length > 0) {
            d |= 0x80;
        }
        r.push_back(d);
    } while (length > 0);

    io->write(r.data(), r.size());
    io->write(data.c_str(), data.size());
    return;
}
<|endoftext|>