X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Packets%2FPacketParser.hh;h=b5ef0ea7a0e7b6dff10a05fb82965bb5ab89027b;hb=5443435c4c2b6e4386c5334b5b8358273f2bae93;hp=cd45e47bbe892a9f50ae043fbf4c2a0aeef2d160;hpb=47368f306a577d1e46df69a7f729bd3893cbe5e7;p=senf.git diff --git a/Packets/PacketParser.hh b/Packets/PacketParser.hh index cd45e47..b5ef0ea 100644 --- a/Packets/PacketParser.hh +++ b/Packets/PacketParser.hh @@ -1,7 +1,9 @@ -// Copyright (C) 2007 -// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) -// Kompetenzzentrum fuer Satelitenkommunikation (SatCom) -// Stefan Bund +// $Id$ +// +// Copyright (C) 2007 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,50 +26,171 @@ /** \defgroup packetparser The PacketParser facility The PacketParser facility provides a framework to implement very lightweight classes which parse - the raw content of a packet into meaningful values. PacketParsers are always passed around by - value, they can be understood as pointers into the data structure with added type information - providing parsing functions. - - Parsers are built hirarchically. A high-level parser will return other parsers when returning - some element (Example: Asking an EthernetParser for the ethertype field by calling the parsers - \c type() member will return an UInt16 parser). The lowest level building blocks then return the + the raw content of a packet into meaningful values. PacketParsers are always passed around + by value, they can be understood as pointers into the packet data with added type + information providing parsing functions. + + Packet parsers are \e only used within the packet framework. You should never allocate a new + parser instance directly, you should the Packet library let that do for you (either by having + the parser as a packet parser in a packet type or by having a member in the packet parser which + allocates the parser as a sub-parser). + + Parsers are built hierarchically. A high-level parser will return other parsers when accessing + an element (Example: Asking an EthernetParser for the ethertype field by calling the parsers \c + type() member will return an \c UInt16 parser). The lowest level building blocks then return the values. This hierarchical structure greatly simplifies building complex parsers. - Every parser is derived from senf::PacketParserBase. This parser provides the necessary - housekeeping information and provides the parsers with access to the data. + Since parsers are very lightweight and are passed by value, packet fields are accessed using the + corresponding accessor method: + \code + SomePacket p (...) + SomePacket q (...) + + // Assign new value to an integer parser + p->someField() = 10; + + // Write out above value + std::cerr << p->someField() << "\n"; + + // Use the generic parser-assignment operator '<<' to copy field values + p->someVector()[1].someOtherField() << q->someField(); + p->someVector() << q->someVector() + \endcode + + Here \c someField(), \c someOtherField() and \c someVector() are accessor methods named after + the field name. Each returns a parser object. Simple parsers can be used like their + corresponding basic type (e.g. a UInt16Parser field can be used like an unsigned integer), more + complex parsers provide type specific access members. Assigning a value to a parser will change + the underlying representation (the packet data). + + Parsers can be grouped into several categories. These categories are not all defined rigorously + but are nevertheless helpful when working with the parsers: + \li \ref parserimpl_value provide the lowest level parsers (e.g. senf::UInt16Parser which + returns an integer value). + \li \ref parserimpl_collection are parsers which model a collection of sub-elements like + senf::ListParser or senf::VectorParser. + \li \ref parserimpl_composite collect several fields of arbitrary type into a new + parser. Parsers defined using the \ref packetparsermacros fall under this category. + \li \ref parserimpl_packet are used to define a packet type. + + \warning Parsers are like iterators: They are invalidated whenever the size of the packet's + data is changed. You should not store a parser anywhere. If you want to keep a parser + reference, use the senf::SafePacketParserWrapper wrapper. You still will need to take extra care to + ensure the parser is not invalidated. + + \section parserimpl Packet parser categories + + Every parser is derived from senf::PacketParserBase. This class provides the necessary + housekeeping information and provides the parsers with access to the data. You may in principle + define arbitrary methods as parser members (e.g. methods to calculate a checksum, methods + processing fields in some way and so on). You should however be very wary to access data outside + the range assigned to the packet (the range starting at \c i() and with a size of senf::bytes() + bytes). + + Each parser type has specific features + + \subsection parserimpl_value Value parsers + + For a parser \a SomeParser to be a value parser, the following expressions must be valid: + \code + // SomeParser must have a 'value_type', The 'value_type' must be default constructible, copy + // constructible and assignable + SomeParser::value_type v; + + // An instance of 'SomeParser' must have a 'value' member which returns a value which may be + // assigned to a variable of type 'value_type' + v = p.someParserField().value() + + // It must be possible to assign a new value using the 'value' member + p.someParserField().value(v) + \endcode + + If at all possible, the 'value_type' should not reference the packet data using iterators or + pointers, it should hold a copy of the value (it's Ok for \c value() to return such a reference + as long as assigning it to a \c value_type variable will copy the value). + + \see parseint + + \subsection parserimpl_collection Collection parsers + + A collection parser \a SomeParser should model STL containers. The parsers themselves will + probably only // provide a reduced interface, but the collection parser should have a \c + collection member which is a wrapper providing the full interface. + \code + SomeParser::container c (p.someParserField()); + \endcode + + You will probably only very seldom need to implement a completely new collection + parser. Instead, you can rely on senf::VectorParser or senf::ListParser and implement new + policies. + + \see parsecollection - The PacketParser facility predefines several parsers to be used as building blocks in defining - more complex parsers (integer parsers, several parsers for repetitive constructs) + \subsection parserimpl_composite Composite parsers + + If possible, composite parsers should be implemented using the \ref packetparsermacros. In + addition to the normal parser requirements, these macros ensure, that for each field, + fieldname_t is a typedef for the fields parser and + fieldname_offset is the offset of the field in bytes from the beginning of the + parser (either a constant for fixed size parsers or a member function for dynamically sized + parsers). When defining composite parsers without the help of the \ref packetparsermacros, you + should provide those same members. + + \subsection parserimpl_packet Protocol parsers + + Protocol parsers are composite parsers with relaxed requirements. Since a Protocol parser will + never be used as a sub-parser (it will not be used within another composite parser or as value + type in a collection parser), the value returned by senf::bytes for this parser must not + necessarily cover the complete packet (e.g. if the packet has a trailer, the trailer will live + outside the range given by senf::bytes). You may define any member you want to have in your + packets field interface. These members may access the packet data in any way. You just need to + ensure, that the integration into the packet-type is correct (the senf::PacketTypeMixin will by + default use senf::bytes() to find the end of the header). + +
*/ -#ifndef HH_PacketParser_ -#define HH_PacketParser_ 1 +#ifndef HH_SENF_Packets_PacketParser_ +#define HH_SENF_Packets_PacketParser_ 1 // Custom includes #include #include #include -#include "Utils/SafeBool.hh" +#include "../Utils/safe_bool.hh" #include "PacketTypes.hh" -#define HH_PacketData_DeclOnly #include "PacketData.hh" -#undef HH_PacketData_DeclOnly +#include "ParseHelpers.hh" -#include "PacketParser.mpp" +//#include "PacketParser.mpp" ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { + + class Packet; - /** \brief Parser Baseclass + /** \brief Parser Base class + + Parsers come in two flavors: fixed and dynamically sized parsers. A fixed size + parser has a constant size, it will always parse a fixed number of bytes. The low-level + 'final' parsers (like the integer parsers) are fixed size parsers as are composite parsers + built up only of fixed-size fields. - To implement a packet parser, you need to derive from PacketParserBase and implement several - required members. There are two ways how to do this. - \li If the parser just consists of a simple sequence of consecutive fields, the - SENF_PACKET_PARESR_DEFINE_FIELDS and SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS macros - provide a simple an convenient way to define the packet + A dynamically sized parser on the other hand infers it's size from the contents of + the data parsed. Any parser containing at least one dynamically sized sub-parser will itself + be dynamically sized. + + Both kinds of parser need to derive from PacketParserBase and implement several required + members. Which members to implement depends on the parsers flavor. There are two ways how to + do this. + \li If the parser just consists of sequence of consecutive fields (sub-parsers), the \ref + packetparsermacros provide a simple yet flexible way to define a packet parser. \li In more complex cases, you need to implement the necessary members manually. - The following example documents the interface (which must be) provided by a parser. + This documentation is about the manual implementation. You should nevertheless read through + this to understand, what above macros are doing. + + The following example documents the interface (which must be) provided by a parser: \code struct FooParser : public PacketParserBase { @@ -85,7 +208,7 @@ namespace senf { // of bytes to allocate when creating a new object static const size_type init_bytes = some_constant_size; - // You also mey define an init() member. This will be called to initialize a newly + // You also may define an init() member. This will be called to initialize a newly // created data object. The default implementation just does nothing. void init() const; @@ -95,19 +218,19 @@ namespace senf { // used to construct the sub-parsers. This member either takes an iterator to the // data to be parsed or just an offset in bytes. - senf::Parse_UInt16 type() const { return parse( 0 ); } - senf::Parse_UInt16 size() const { return parse( 2 ); } + senf::UInt16Parser type() const { return parse( 0 ); } + senf::UInt16Parser size() const { return parse( 2 ); } }; \endcode You should never call the \c bytes() member of a parser directly. Instead you should use the - freestanding senf::bytes() functon. This function will return the correct size even for - fixed-size parsers. You may access \c fixed_bytes directly, however be aware that this will - restrict your code to fixed size parsers (which depending on the circumstances may be + freestanding senf::bytes() function. This function will return the correct size irrespective + of the parsers flavor. You may access \c fixed_bytes directly, however be aware that this + will restrict your code to fixed size parsers (which depending on the circumstances may be exactly what you want). - In the same way, dont access \c init_bytes directly, always use the senf::init_bytes - metafunction class which will correctly support fixed size parsers. + In the same way, don't access \c init_bytes directly, always use the senf::init_bytes + meta-function class which correctly supports fixed size parsers. \ingroup packetparser */ @@ -117,11 +240,12 @@ namespace senf { /////////////////////////////////////////////////////////////////////////// // Types - typedef detail::packet::iterator data_iterator; - typedef detail::packet::size_type size_type; - typedef detail::packet::difference_type difference_type; - typedef detail::packet::byte byte; - typedef PacketData * state_type; + typedef detail::packet::iterator data_iterator; ///< Raw data iterator type + typedef detail::packet::size_type size_type; ///< Unsigned integral type + typedef detail::packet::difference_type difference_type; ///< Signed integral type + typedef detail::packet::byte byte; ///< Unsigned 8bit value, the raw value type + typedef PacketData * state_type; ///< Type of the 'state' parameter + typedef PacketParserBase parser_base_type; ///< Base type of the next parser /////////////////////////////////////////////////////////////////////////// ///\name Structors and default members @@ -135,31 +259,118 @@ namespace senf { ///@} /////////////////////////////////////////////////////////////////////////// - data_iterator i() const; - state_type state() const; - PacketData & data() const; - - void init() const; + data_iterator i() const; ///< Return beginning of data to parse + /**< The parser is expected to interpret the data beginning + here. The size of the interpreted is given by + senf::bytes(parser + instance). */ + + data_iterator i(size_type offset) const; ///< Return iterator \a offset bytes from the start + /**< The return value is the same as i() + \a + offset. However, the parser checks, that the iterator is + still within range of the raw data + container. Otherwise a TruncatedPacketException is + thrown. + + \throws TruncatedPacketException if the raw data + container does not hold at least \a offset bytes + starting at i(). */ + + state_type state() const; ///< Return state of this parser + /**< The value returned should be interpreted as an opaque + value provided just to be forwarded to other + parsers. */ + + PacketData & data() const; ///< Access the packets raw data container + /**< This member will return the raw data container holding + the data which is parsed by \c this parser. */ + + void init() const; ///< Default implementation + /**< This is just an empty default + implementation. Re-implement this member in your own + parsers if needed. */ protected: - PacketParserBase(data_iterator i, state_type s); - PacketParserBase(data_iterator i, state_type s, size_type size); - - bool check(size_type size); - void validate(size_type size); - - template Parser parse(data_iterator i) const; - template Parser parse(size_type n) const; - - void defaultInit() const; + PacketParserBase(data_iterator i, state_type s); ///< Standard constructor + /**< This is the constructor used by most parsers. The + parameters are just forwarded from the derived classes + constructor parameters. */ + + PacketParserBase(data_iterator i, state_type s, size_type size); + ///< Size checking constructor + /**< In addition to the standard constructor, this + constructor will validate, that there is enough data in + the raw data container to parse \a size bytes after \a + i. + + This constructor is called by all 'final' parsers + (e.g. the integer parsers) and \e only by those + parsers. Most parsers do \e not check the validity of + the iterator, this is delayed until the very last + parser. This allows to partial parse truncated + packets. + + \throw TruncatedPacketException if the raw data + container does not hold at least \a size bytes + beginning at \a i. */ + + bool check(size_type size) const; ///< Check size of data container + /**< \returns \c true, if the data container holds at least + \a size beginning at i(), \c false otherwise. */ + + void validate(size_type size) const; ///< Validate size of data container + /**< \throws TruncatedPacketException if the raw data + container does not hold at least \a size bytes + beginning at i(). */ + + template Parser parse(data_iterator i) const; ///< Create sub-parser + /**< Creates a new instance of \a Parser to parse data + beginning at \a i. Automatically passes \a state() to + the new parser. */ + + template Parser parse(Arg const & arg, data_iterator i) const; + ///< Create sub-parser + /**< This is like parse(data_iterator), however it passes + the extra argument \a arg to the \a Parser + constructor. */ + + template Parser parse(size_type n) const; ///< Create sub-parser + /**< Creates a new instance of \a Parser to parse data + * beginning at i() + \a n. Automatically passes \a + state() to the new parser. */ + + template Parser parse(Arg const & arg, size_type n) const; + ///< Create sub-parser + /**< This is like parse(size_type), however it passes the + extra argument \a arg to the \a Parser constructor. */ + + void defaultInit() const; ///< Default implementation + /**< This is just an empty default + implementation. Re-implement this member in your own + parsers if needed. */ + + Packet packet() const; ///< Get packet this parser is parsing from + /**< \note This member should only be used from packet + parsers when access to previous or following packets is + needed e.g. for calculating checksums etc. */ + + void resize(size_type oldSize, size_type newSize); ///< Resize data container + /**< This command will erase or insert bytes from/into the + data container at the end of the parser (at i() + \a + newSize). If \a oldSize is > \a newSize, bytes will be + removed, otherwise bytes will be inserted. + + \warning This may invalidate iterators and other + parsers. The current parser itself is automatically + updated */ private: - data_iterator end(); + data_iterator end() const; data_iterator i_; PacketData * data_; - template friend class SafePacketParser; + template friend class SafePacketParserWrapper; }; /** \brief Return raw size parsed by the given parser object @@ -180,19 +391,20 @@ namespace senf { PacketParserBase::size_type bytes(Parser p); namespace detail { template class ParserInitBytes; } + namespace detail { template class ParserIsFixed; } /** \brief Return number of bytes to allocate to new object of given type - This metafcuntion is called like + This meta-function is called like \code senf::init_bytes::value \endcode - This expression evaluates to a compile-time constant itegral expression of type - senf::PacketParserBase::size_type. This metafunction will return \c Parser::fixed_bytes or + This expression evaluates to a compile-time constant integral expression of type + senf::PacketParserBase::size_type. This meta-function will return \c Parser::fixed_bytes or \c Parser::init_bytes depending on the type of parser. - \param[in] Parser Parser to return init_bytes for + \param[in] Parser The Parser to return init_bytes for \returns Number of bytes to allocate to the new object \ingroup packetparser */ @@ -200,219 +412,105 @@ namespace senf { struct init_bytes : public detail::ParserInitBytes {}; + /** \brief Test, whether a parser is a fixed-size parser + + This meta-function is called like + \code + senf::is_fixed::value + \endcode + + This expression evaluates to a compile-time constant boolean expression which is \c true, if + \a SomeParser is a fixed size parser, \c false otherwise + + \param[in] Parser The Parser to test + \returns \c true, if \a Parser is fixed size, \c false otherwise + \ingroup packetparser + */ + template + struct is_fixed : public detail::ParserIsFixed + {}; + +# ifndef DOXYGEN template typename boost::enable_if< boost::is_base_of, Parser >::type operator<<(Parser target, Parser source); +# else + /** \brief Generic parser copying + + + This operator allows to copy the values of identical parsers. This operation does \e not + depend on the parsers detailed implementation, it will just replace the data bytes of the + target parser with those from the source parser. This allows to easily copy around complex + packet substructures. + + This operation is different from the ordinary assignment operator: It does not change the \a + target parser, it changes the data referenced by the \a target parser. + + \ingroup packetparser + */ + template + Parser operator<<(Parser target, Parser source); +# endif +# ifndef DOXYGEN template typename boost::enable_if_c < boost::is_base_of::value && ! boost::is_base_of::value, Parser >::type operator<<(Parser target, Value const & value); +# else + /** \brief Generic parser value assignment - /** \defgroup packetparsermacros Helper macros for defining new packet parsers - - To simplify the definition of simple packet parsers, several macros are provided. Before - using these macros you should familarize yourself with the packet parser interface as - described in senf::PacketParserBase. - - These macros simplify providing the above defined interface. A typical packet declaration - using these macros has the following form (This is a concrete example from the definition of - the ethernet packet in DefaultBundle//EthernetPacket.hh) - \code - struct Parse_EthVLan : public senf::PacketParserBase - { - SENF_PACKET_PARSER_INIT(Parse_EthVLan); - - // //////////////////////////////////////////////////////////////////////// - - typedef senf::Parse_UIntField < 0, 3 > Parse_Priority; - typedef senf::Parse_Flag < 3 > Parse_CFI; - typedef senf::Parse_UIntField < 4, 16 > Parse_VLanId; - typedef senf::Parse_UInt16 Parse_Type; - - SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS( - ((OverlayField)( priority, Parse_Priority )) - ((OverlayField)( cfi, Parse_CFI )) - ((Field )( vlanId, Parse_VLanId )) - ((Field )( type, Parse_Type )) - ); - }; - \endcode - - The macros take care of the following: - \li They define the accessor functions returning parsers of the given type. - \li They automatically calculate the offset of the fields from the preceding fields. - \li The macros provide a definition for \c init() - \li The macros define the \c bytes(), \c fixed_bytes and \c init_bytes members as needed. + This operator allows to assign a value to parsers which implement a value(\a + value) member. This operator allows to use a common syntax for assigning values or + parsers to a parser. - You may define either a fixed or a dynamically sized parser. Fixed size parsers are defined - using \ref SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS, dynamically sized parsers are defined - using \ref SENF_PACKET_PARSER_DEFINE_FIELDS. The different members are implemented such - that: - - \li The needed parser constructor is defined - \li \c init() calls \c defaultInit(). \c defaultInit() is defined to call \c init() on each - of the fields. - \li \c bytes() (on dynamically sized parser) respectively \c fixed_bytes (on fixed size - parsers) is defined to return the sum of the sizes of all fields. - \li On dynamically sized parsers, \c init_bytes is defined to return the sum of the - \c init_size's of all fields - - The central definition macros are \ref SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS and \ref - SENF_PACKET_PARSER_DEFINE_FIELDS. The argument to both has the same structure. It is a - (boost preprocessor style) sequence of field definitions where each field definition - provides the builder macro to use and the name and type of the field to define: - \code - SENF_PACKET_PARSER_DEFINE[_FIXED]_FIELDS( - (( )( , )) - ... - ) - \endcode - - The \a builder argument selects, how the field is defined - \li Field defines a field and increments the current position by the size of the - field - \li OverlayField defines a field like Field but does \e not increment the - position. In the above example, this is used to overlay the different bitfield parsers: - All overlaying bitfield parser except the last one (the one with the highest bit - numbers) is marked as OverlayField. - - The \a name argument defines the name of the accessor method. - - The \a type argument is the parser to return for that field. Since none of the arguments may - contain a komma, This argument cannot be a template. Always use typedefs to access - tempalte parsers as shown above. - - The \ref SENF_PACKET_PARSER_INIT makro defines the constructor and the \c init() member. If - you want to provide your own \c init() implementation, use \ref - SENF_PACKET_PARSER_NO_INIT. The first statement in your init method should probably to call - \c defaultInit(). This will call the \c init() member of all the fields. Afterwards you can - set up the field values as needed: - \code - struct SomePacket : public senf::PacketParserBase - { - SENF_PACKET_PARSER_NO_INIT(SomePacket); - - typedef senf::Parse_UInt8 Parse_Type; - typedef senf::Parse_Vector< senf::Parse_UInt32, - senf::SimpleVectorSizer - > Parse_Elements; - - SENF_PACKET_PARSER_DEFINE_FIELDS( - ((Field)( type, Parse_Type )) - ((Field)( elements, Parse_Elements )) - ); - - void init() const { - defaultInit(); - type() = 0x01; - elements().push_back(0x01020304u); - } - } - \endcode - \ingroup packetparser */ + template + Parser operator<<(Parser target, Value const & value); +# endif - /** \brief Define initialization members of a parser - - This macro defines the packet parser constructor and the \c init() member. \c init() is - defined to just call \c defaultInit() which is defined by the other macros to call \c init() - on each of the parsers fields. - - \ingroup packetparsermacros - \hideinitializer - */ -# define SENF_PACKET_PARSER_INIT(name) \ - name(data_iterator i, state_type s) : senf::PacketParserBase(i,s) {} \ - void init() const { defaultInit(); } - - /** \brief Define initialization mebers of a parser except init() - - This macro is like SENF_PACKET_PARSER_INIT but does \e not define \c init(). This allows you - to provide your own implementation. You should call \c defaultInit() first before - initializing your data fields. - - \ingroup packetparsermacros - \hideinitializer - */ -# define SENF_PACKET_PARSER_NO_INIT(name) \ - name(data_iterator i, state_type s) : senf::PacketParserBase(i,s) {} +# ifndef DOXYGEN + template + typename boost::enable_if_c < + boost::is_base_of::value + && ! boost::is_base_of::value, + Parser >::type + operator<<(Parser target, boost::optional const & value); +# else + /** \brief Generic parser value assignment - /** \brief Define fields for a dynamically sized parser + This operator allows to assign a value to parsers which implement a value(\a + value) member. This special version allows to assign optional values: IF the + optional value is not set, the assignment will be skipped. - Define the fields as specified in \a fields. This macro supports dynamically sized - subfields, the resulting parser will be dynamically sized. + This operator allows to use a common syntax for assigning values or parsers to a parser. - \ingroup packetparsermacros - \hideinitializer + \ingroup packetparser */ -# define SENF_PACKET_PARSER_DEFINE_FIELDS(fields) \ - SENF_PACKET_PARSER_I_DEFINE_FIELDS(fields) - - /** \brief Define fields for a fixed size parser - - Define the fields as specified in \a fields. This macro only supports fixed size - subfields, the resulting parser will also be a fixed size parser. + template + Parser operator<<(Parser target, boost::optional const & value); +# endif - \ingroup packetparsermacros - \hideinitializer + /** \brief Default parser parsing nothing */ -# define SENF_PACKET_PARSER_DEFINE_FIXED_FIELDS(fields) \ - SENF_PACKET_PARSER_I_DEFINE_FIXED_FIELDS(fields) - struct VoidPacketParser : public PacketParserBase { - SENF_PACKET_PARSER_INIT(VoidPacketParser); - }; - - /** \brief - */ - template - class SafePacketParser - : public SafeBool< SafePacketParser > - { - public: - /////////////////////////////////////////////////////////////////////////// - // Types - - /////////////////////////////////////////////////////////////////////////// - ///\name Structors and default members - ///@{ - - // default default constructor - // default copy constructor - // default copy assignment - // default destructor - SafePacketParser(); - - // conversion constructors - SafePacketParser(Parser parser); - - SafePacketParser & operator=(Parser parser); - - ///@} - /////////////////////////////////////////////////////////////////////////// - - Parser operator*() const; - Parser const * operator->() const; - bool boolean_test() const; - - protected: - - private: - mutable boost::optional parser_; - senf::safe_data_iterator i_; +# include SENF_FIXED_PARSER() + SENF_PARSER_FINALIZE(VoidPacketParser); }; } ///////////////////////////////hh.e//////////////////////////////////////// +#endif +#if !defined(HH_SENF_Packets_Packets__decls_) && !defined(HH_SENF_Packets_PacketParser_i_) +#define HH_SENF_Packets_PacketParser_i_ #include "PacketParser.cci" #include "PacketParser.ct" #include "PacketParser.cti" @@ -425,4 +523,7 @@ namespace senf { // c-file-style: "senf" // indent-tabs-mode: nil // ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// comment-column: 40 // End: +