corelib/src/SRDBinary.cc

568 lines
12 KiB
C++
Raw Normal View History

/******************************************************************************/
/* SRD PARSER AND PREPROCESSOR - BINARY FORM **********************************/
/******************************************************************************/
#include <lw/lib/SRDBinary.hh>
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 );
}
}