/******************************************************************************/ /* SRD PARSER AND PREPROCESSOR - BINARY FORM **********************************/ /******************************************************************************/ #include using namespace lw; /*= MAGIC NUMBERS, VERSIONS AND TAGS =========================================*/ namespace { // Magic number for binary SRD static constexpr uint32_t C_MAGIC_ = 0xea7ca1ce; // E_Versions_ - Binary SRD version numbers enum class E_Version_ : uint32_t { V1 = 0xc0ffee00 , }; // E_V1Tag_ - Version 1 SRD tags enum class E_V1Tag_ : uint8_t { END = 0x00 , LIST = 0x01 , WORD_NEW = 0x02 , WORD_KNOWN = 0x03 , VAR_WORD = 0x04 , STRING = 0x05 , INT = 0x06 , LONG = 0x07 , FLOAT = 0x08 , COMMENT = 0x09 , BINARY = 0x0a , }; } // namespace namespace lw { // Writer for version numbers template< > struct T_ObjectWriter< E_Version_ > { static void write( T_BinaryWriter const& writer , E_Version_ const& v ) { writer.write( ( uint32_t ) v ); } }; // Writer for version 1 tags template< > struct T_ObjectWriter< E_V1Tag_ > { static void write( T_BinaryWriter const& writer , E_V1Tag_ const& v ) { writer.write( ( uint8_t ) v ); } }; // Reader for version 1 tags template< > struct T_ObjectReader< E_V1Tag_ > { static E_V1Tag_ read( T_BinaryReader const& reader ) { return ( E_V1Tag_ ) reader.read< uint8_t >( ); } }; } // namespace /*= VERSION 1 BINARY READER ==================================================*/ namespace { class T_SRDBRVersion1_ { private: T_BinaryReader& reader_; T_String const& name_; T_SRDErrors& errors_; A_SRDReaderTarget& target_; T_HashIndex wordsIndex_; T_Array< T_String > words_; uint32_t lastTagPos_; typedef std::function< T_SRDToken( T_String const& ) > F_StringTok; size_t curPos( ) const { return reader_.stream( ).position( ); } void push( T_SRDToken&& token ) { token.location( name_ , lastTagPos_ ); target_.push( errors_ , std::move( token ) ); } void readNewWord( F_StringTok mt ); void readKnownWord( F_StringTok mt ); void readString( F_StringTok mt ); void readLoop( ); public: T_SRDBRVersion1_( T_BinaryReader& reader , T_String const& name , T_SRDErrors& errors , A_SRDReaderTarget& target ) : reader_( reader ) , name_( name ) , errors_( errors ) , target_( target ) { } void read( ); }; /*----------------------------------------------------------------------------*/ void T_SRDBRVersion1_::readNewWord( F_StringTok mt ) { const auto wordPos( curPos( ) ); const T_String word( reader_.read< T_String >( ).usePool( ) ); if ( word.size( ) == 0 ) { errors_.add( "empty word" , name_ , uint32_t( wordPos ) ); return; } const bool valid = T_SRDToken::IsWord( word ); if ( !valid ) { errors_.add( "invalid word" , name_ , wordPos ); } const auto hash( ComputeHash( word ) ); uint32_t index( wordsIndex_.first( hash ) ); while ( index != T_HashIndex::INVALID_INDEX ) { if ( words_[ index ] == word ) { errors_.add( "duplicate word" , name_ , wordPos ); return; } index = wordsIndex_.next( index ); } wordsIndex_.add( hash ); words_.add( word ); if ( valid ) { push( mt( word ) ); } } void T_SRDBRVersion1_::readKnownWord( F_StringTok mt ) { const auto pos( curPos( ) ); const auto widx( reader_.read< uint32_t >( ) ); if ( widx >= words_.size( ) ) { errors_.add( "unregistered word" , name_ , pos ); return; } T_String const& word( words_[ widx ] ); if ( T_SRDToken::IsWord( word ) ) { push( mt( word ) ); } } void T_SRDBRVersion1_::readString( F_StringTok mt ) { const auto str( reader_.read< T_String >( ).usePool( ) ); push( mt( str ) ); } /*----------------------------------------------------------------------------*/ void T_SRDBRVersion1_::readLoop( ) { bool pendingVariable = false; uint32_t depth = 1; while ( depth > 0 ) { lastTagPos_ = reader_.stream( ).position( ); const auto tag( reader_.read< E_V1Tag_ >( ) ); if ( pendingVariable && tag != E_V1Tag_::WORD_NEW && tag != E_V1Tag_::WORD_KNOWN ) { pendingVariable = false; errors_.add( "spurious variable tag" , name_ , lastTagPos_ - 1 ); } switch ( tag ) { case E_V1Tag_::LIST: depth ++; push( T_SRDToken::ListStart( ) ); break; case E_V1Tag_::END: depth --; if ( depth > 0 ) { push( T_SRDToken::ListEnd( ) ); } break; case E_V1Tag_::WORD_NEW: if ( pendingVariable ) { lastTagPos_ --; readNewWord( T_SRDToken::Variable ); pendingVariable = false; } else { readNewWord( T_SRDToken::Word ); } break; case E_V1Tag_::WORD_KNOWN: if ( pendingVariable ) { lastTagPos_ --; readKnownWord( T_SRDToken::Variable ); pendingVariable = false; } else { readKnownWord( T_SRDToken::Word ); } break; case E_V1Tag_::VAR_WORD: pendingVariable = true; break; case E_V1Tag_::STRING: readString( T_SRDToken::String ); break; case E_V1Tag_::COMMENT: readString( T_SRDToken::Comment ); break; case E_V1Tag_::INT: push( T_SRDToken::Integer( reader_.read< int32_t >( ) ) ); break; case E_V1Tag_::LONG: push( T_SRDToken::Long( reader_.read< int64_t >( ) ) ); break; case E_V1Tag_::FLOAT: push( T_SRDToken::Float( reader_.read< double >( ) ) ); break; case E_V1Tag_::BINARY: { const uint32_t size( reader_.read< uint32_t >( ) ); T_SharedPtr< T_Buffer< uint8_t > > buffer( NewShared< T_Buffer< uint8_t > >( size ) ); if ( size ) { reader_.stream( ).read( buffer->data( ) , size ); } push( T_SRDToken::Binary( buffer ) ); } break; } } } void T_SRDBRVersion1_::read( ) { T_SRDReaderTargetHelper rth( target_ , errors_ ); try { readLoop( ); } catch ( X_StreamError const& e ) { switch ( e.code( ) ) { case E_StreamError::BAD_DATA: errors_.add( "invalid data" , name_ , reader_.stream( ).position( ) ); break; case E_StreamError::END: errors_.add( "unexpected end of file" , name_ , reader_.stream( ).position( ) ); break; default: errors_.add( "read error" , name_ , reader_.stream( ).position( ) ); break; } } } } // namespace /*= T_SRDBinaryWriter ========================================================*/ T_SRDBinaryWriter::T_SRDBinaryWriter( A_OutputStream& output ) : writer_( output , E_Endian::LITTLE ) , depth_( 0 ) { } /*----------------------------------------------------------------------------*/ void T_SRDBinaryWriter::flushComment( ) { if ( comment_.size( ) != 0 ) { writer_.write( E_V1Tag_::COMMENT ); writer_.write( comment_ ); comment_.clear( ); } } void T_SRDBinaryWriter::writeWord( T_String const& word ) { // Try to find the word in the index const uint32_t hash = ComputeHash( word ); uint32_t index = wordsIndex_.first( hash ); while ( index != T_HashIndex::INVALID_INDEX ) { if ( word == words_[ index ] ) { break; } index = wordsIndex_.next( index ); } if ( index == T_HashIndex::INVALID_INDEX ) { // New word wordsIndex_.add( hash ); words_.add( word ); writer_.write( E_V1Tag_::WORD_NEW ); writer_.write( word ); } else { // Known word, use the index writer_.write( E_V1Tag_::WORD_KNOWN ); writer_.write( index ); } } /*----------------------------------------------------------------------------*/ T_SRDBinaryWriter& T_SRDBinaryWriter::start( ) { if ( depth_ != 0 ) { throw X_SRDWriterError( "already started" ); } writer_.write( C_MAGIC_ ); writer_.write( E_Version_::V1 ); depth_ = 1; return *this; } T_SRDBinaryWriter& T_SRDBinaryWriter::end( ) { if ( depth_ != 1 ) { throw X_SRDWriterError( depth_ == 0 ? "already ended" : "unterminated lists" ); } flushComment( ); writer_.write( E_V1Tag_::END ); depth_ = 0; comment_.free( ); wordsIndex_.free( ); words_.free( ); return *this; } /*----------------------------------------------------------------------------*/ T_SRDBinaryWriter& T_SRDBinaryWriter::startList( ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::LIST ); depth_ ++; return *this; } T_SRDBinaryWriter& T_SRDBinaryWriter::endList( ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::END ); depth_ --; return *this; } /*----------------------------------------------------------------------------*/ T_SRDBinaryWriter& T_SRDBinaryWriter::putText( T_String const& text ) { if ( T_SRDToken::IsWord( text ) ) { return putWord( text ); } else { return putString( text ); } } T_SRDBinaryWriter& T_SRDBinaryWriter::putWord( T_String const& word ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writeWord( word ); return *this; } T_SRDBinaryWriter& T_SRDBinaryWriter::putString( T_String const& string ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::STRING ); writer_.write( string ); return *this; } T_SRDBinaryWriter& T_SRDBinaryWriter::putComment( T_String const& comment ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } comment_ << comment; return *this; } T_SRDBinaryWriter& T_SRDBinaryWriter::putVariable( T_String const& variable ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::VAR_WORD ); writeWord( variable ); return *this; } /*----------------------------------------------------------------------------*/ T_SRDBinaryWriter& T_SRDBinaryWriter::putBinary( T_Buffer< uint8_t > const& binary ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::BINARY ); writer_.write( uint32_t( binary.bytes( ) ) ); if ( binary.size( ) != 0 ) { writer_.stream( ).write( binary.data( ) , binary.bytes( ) ); } return *this; } /*----------------------------------------------------------------------------*/ T_SRDBinaryWriter& T_SRDBinaryWriter::putInteger( int64_t value ) { if ( value >= INT32_MIN && value <= INT32_MAX ) { return putInt32( int32_t( value ) ); } else { return putInt64( int64_t( value ) ); } } T_SRDBinaryWriter& T_SRDBinaryWriter::putInt32( int32_t value ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::INT ); writer_.write( value ); return *this; } T_SRDBinaryWriter& T_SRDBinaryWriter::putInt64( int64_t value ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::LONG ); writer_.write( value ); return *this; } /*----------------------------------------------------------------------------*/ T_SRDBinaryWriter& T_SRDBinaryWriter::putFloat( double value ) { if ( depth_ == 0 ) { throw X_SRDWriterError( "not started" ); } flushComment( ); writer_.write( E_V1Tag_::FLOAT ); writer_.write( value ); return *this; } /*= T_SRDBinaryReader ========================================================*/ void T_SRDBinaryReader::read( T_String const& name , A_InputStream& input ) { T_BinaryReader rd( input , E_Endian::LITTLE ); T_SRDErrors errors; // Read/check magic number const auto magicPos( input.position( ) ); const uint32_t magic( ([&]( ) { try { return rd.read< uint32_t >( ); } catch ( X_StreamError& e ) { switch ( e.code( ) ) { case E_StreamError::BAD_DATA: case E_StreamError::END: errors.add( "missing magic" , name , input.position( ) ); break; default: errors.add( "read error" , name , input.position( ) ); break; } throw X_SRDErrors( errors ); } })( ) ); if ( magic != C_MAGIC_ ) { errors.add( "invalid magic number" , name , magicPos ); throw X_SRDErrors( errors ); } // Read version number const uint32_t versionPos( input.position( ) ); const uint32_t version( ([&]( ) { try { return rd.read< uint32_t >( ); } catch ( X_StreamError& e ) { switch ( e.code( ) ) { case E_StreamError::BAD_DATA: case E_StreamError::END: errors.add( "missing version ID" , name , input.position( ) ); break; default: errors.add( "read error" , name , input.position( ) ); break; } throw X_SRDErrors( errors ); } })( ) ); switch ( version ) { case uint32_t( E_Version_::V1 ): T_SRDBRVersion1_( rd , name , errors , target_ ).read( ); break; default: errors.add( "invalid version ID" , name , versionPos ); break; } if ( errors.size( ) != 0 ) { throw X_SRDErrors( errors ); } }