Add char* constructor to string_view

Mostly relevant for testing, this enables string_view to work as
expected with string literals.
This commit is contained in:
Jørgen Kvalsvik
2016-04-12 16:00:37 +02:00
parent bfa3f799b9
commit 6b64796d49
8 changed files with 64 additions and 94 deletions

View File

@@ -647,7 +647,7 @@ bool Parser::parseState(std::shared_ptr<ParserState> parserState) const {
return true;
}
}
parserState->rawKeyword->addRawRecordString(line.string());
parserState->rawKeyword->addRawRecordString(line);
}
if (parserState->rawKeyword

View File

@@ -127,15 +127,14 @@ static ParserRecordPtr createSimpleParserRecord() {
BOOST_AUTO_TEST_CASE(parse_validRecord_noThrow) {
ParserRecordPtr record = createSimpleParserRecord();
RawRecord rawRecord( "100 443 /" );
ParseContext parseContext;
rawRecord.dump();
BOOST_CHECK_NO_THROW(record->parse(parseContext , rawRecord));
RawRecord raw( string_view( "100 443" ) );
BOOST_CHECK_NO_THROW(record->parse(parseContext, raw ) );
}
BOOST_AUTO_TEST_CASE(parse_validRecord_deckRecordCreated) {
ParserRecordPtr record = createSimpleParserRecord();
RawRecord rawRecord( "100 443 /" );
RawRecord rawRecord( string_view( "100 443" ) );
ParseContext parseContext;
const auto deckRecord = record->parse(parseContext , rawRecord);
BOOST_CHECK_EQUAL(2U, deckRecord.size());
@@ -168,7 +167,7 @@ static ParserRecordPtr createMixedParserRecord() {
BOOST_AUTO_TEST_CASE(parse_validMixedRecord_noThrow) {
ParserRecordPtr record = createMixedParserRecord();
RawRecord rawRecord( "1 2 10.0 20.0 4 90.0 /" );
RawRecord rawRecord( string_view( "1 2 10.0 20.0 4 90.0") );
ParseContext parseContext;
BOOST_CHECK_NO_THROW(record->parse(parseContext , rawRecord));
}
@@ -217,7 +216,7 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
// according to the RM, this is invalid ("an asterisk by itself is not sufficient"),
// but it seems to appear in the wild. Thus, we interpret this as "1*"...
{
RawRecord rawRecord( "* /" );
RawRecord rawRecord( "* " );
const auto deckStringItem = itemString->scan(rawRecord);
const auto deckIntItem = itemInt->scan(rawRecord);
const auto deckDoubleItem = itemDouble->scan(rawRecord);
@@ -232,7 +231,7 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
}
{
RawRecord rawRecord( "/" );
RawRecord rawRecord( "" );
const auto deckStringItem = itemString->scan(rawRecord);
const auto deckIntItem = itemInt->scan(rawRecord);
const auto deckDoubleItem = itemDouble->scan(rawRecord);
@@ -248,7 +247,7 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
{
RawRecord rawRecord( "TRYGVE 10 2.9 /" );
RawRecord rawRecord( "TRYGVE 10 2.9 " );
// let the raw record be "consumed" by the items. Note that the scan() method
// modifies the rawRecord object!
@@ -267,7 +266,7 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
// again this is invalid according to the RM, but it is used anyway in the wild...
{
RawRecord rawRecord( "* * * /" );
RawRecord rawRecord( "* * *" );
const auto deckStringItem = itemString->scan(rawRecord);
const auto deckIntItem = itemInt->scan(rawRecord);
const auto deckDoubleItem = itemDouble->scan(rawRecord);
@@ -282,7 +281,7 @@ BOOST_AUTO_TEST_CASE(ParseWithDefault_defaultAppliedCorrectInDeck) {
}
{
RawRecord rawRecord( "3* /" );
RawRecord rawRecord( "3*" );
const auto deckStringItem = itemString->scan(rawRecord);
const auto deckIntItem = itemInt->scan(rawRecord);
const auto deckDoubleItem = itemDouble->scan(rawRecord);
@@ -309,13 +308,13 @@ BOOST_AUTO_TEST_CASE(Parse_RawRecordTooManyItems_Throws) {
parserRecord->addItem(itemK);
RawRecord rawRecord( "3 3 3 /" );
RawRecord rawRecord( "3 3 3 " );
BOOST_CHECK_NO_THROW(parserRecord->parse(parseContext , rawRecord));
RawRecord rawRecordOneExtra( "3 3 3 4 /" );
RawRecord rawRecordOneExtra( "3 3 3 4 " );
BOOST_CHECK_THROW(parserRecord->parse(parseContext , rawRecordOneExtra), std::invalid_argument);
RawRecord rawRecordForgotRecordTerminator( "3 3 3 \n 4 4 4 /" );
RawRecord rawRecordForgotRecordTerminator( "3 3 3 \n 4 4 4 " );
BOOST_CHECK_THROW(parserRecord->parse(parseContext , rawRecordForgotRecordTerminator), std::invalid_argument);
}

View File

@@ -36,7 +36,7 @@ namespace Opm {
const unsigned int maxKeywordLength = 8;
static inline bool is_separator( char ch ) {
return ch == '\t' || ch == ' ';
return ch == '\t' || ch == ' ' || ch == '\n';
}
static inline bool is_quote( char ch ) {

View File

@@ -26,8 +26,10 @@
namespace Opm {
static const std::string emptystr = "";
RawKeyword::RawKeyword(const string_view& name, Raw::KeywordSizeEnum sizeType , const std::string& filename, size_t lineNR) {
RawKeyword::RawKeyword(const string_view& name, Raw::KeywordSizeEnum sizeType , const std::string& filename, size_t lineNR) :
m_partialRecordString( emptystr ) {
if (sizeType == Raw::SLASH_TERMINATED || sizeType == Raw::UNKNOWN) {
commonInit(name.string(),filename,lineNR);
m_sizeType = sizeType;
@@ -74,17 +76,16 @@ namespace Opm {
return m_records.size();
}
static inline bool isTerminator( const std::string& line ) {
auto fst = std::find_if_not( line.begin(), line.end(),
RawConsts::is_separator );
return fst != line.end() && *fst == RawConsts::slash;
static inline bool isTerminator( const string_view& line ) {
return line.size() == 1 && line.back() == RawConsts::slash;
}
/// Important method, being repeatedly called. When a record is terminated,
/// it is added to the list of records, and a new record is started.
void RawKeyword::addRawRecordString(const std::string& partialRecordString) {
m_partialRecordString += " " + partialRecordString;
void RawKeyword::addRawRecordString(const string_view& partialRecordString) {
if( m_partialRecordString == emptystr ) m_partialRecordString = partialRecordString;
else m_partialRecordString = { m_partialRecordString.begin(), partialRecordString.end() };
if( m_sizeType != Raw::FIXED && isTerminator( m_partialRecordString ) ) {
@@ -92,12 +93,12 @@ namespace Opm {
m_currentNumTables += 1;
if (m_currentNumTables == m_numTables) {
m_isFinished = true;
m_partialRecordString.clear();
m_partialRecordString = emptystr;
return;
}
} else if( m_sizeType != Raw::UNKNOWN ) {
m_isFinished = true;
m_partialRecordString.clear();
m_partialRecordString = emptystr;
return;
}
}
@@ -107,14 +108,21 @@ namespace Opm {
if( this->getKeywordName() == "TITLE"
|| RawRecord::isTerminatedRecordString( partialRecordString ) ) {
m_records.emplace_back( std::move( m_partialRecordString ), m_filename, m_name );
m_partialRecordString.clear();
auto recstr = partialRecordString.back() == '/'
? string_view{ m_partialRecordString.begin(), m_partialRecordString.end() - 1 }
: m_partialRecordString;
m_records.emplace_back( recstr, m_filename, m_name );
m_partialRecordString = emptystr;
if( m_sizeType == Raw::FIXED && m_records.size() == m_fixedSize )
m_isFinished = true;
}
}
void RawKeyword::addRawRecordString(const std::string& rec ) {
addRawRecordString( string_view( rec ) );
}
const RawRecord& RawKeyword::getFirstRecord() const {
return *m_records.begin();

View File

@@ -26,6 +26,7 @@
#include <list>
#include <opm/parser/eclipse/RawDeck/RawEnums.hpp>
#include <opm/parser/eclipse/Utility/Stringview.hpp>
namespace Opm {
@@ -46,7 +47,9 @@ namespace Opm {
RawKeyword(const std::string& name , const std::string& filename, size_t lineNR , size_t inputSize , bool isTableCollection = false);
const std::string& getKeywordName() const;
void addRawRecordString(const std::string& partialRecordString);
void addRawRecordString( const string_view& );
/* The string overload exists for testing only */
void addRawRecordString( const std::string& );
size_t size() const;
Raw::KeywordSizeEnum getSizeType() const;
@@ -82,7 +85,7 @@ namespace Opm {
size_t m_currentNumTables;
std::string m_name;
std::list< RawRecord > m_records;
std::string m_partialRecordString;
string_view m_partialRecordString;
size_t m_lineNR;
std::string m_filename;

View File

@@ -33,45 +33,13 @@ using namespace std;
namespace Opm {
static inline std::string::const_iterator findTerminatingSlash( const std::string& rec ) {
if( rec.back() == RawConsts::slash ) return rec.end() - 1;
/* possible fast path: no terminating slash in record */
const auto rslash = std::find( rec.rbegin(), rec.rend(), RawConsts::slash );
if( rslash == rec.rend() ) return rec.end();
/* heuristic: since slash makes everything to the right of it into a
* no-op, if there is more than whitespace to its right before newline
* we guess that this is in fact not the terminating slash but rather
* some comment or description. Most of the time this is not the case,
* and it is slower, so avoid doing it if possible.
*/
auto slash = (rslash + 1).base();
if( std::find_if_not( slash, rec.end(), RawConsts::is_separator ) == rec.end() )
return slash;
/*
* left-to-right search after last closing quote. Like the previous
* implementation, assumes there are no quote marks past the
* terminating slash (as per the Eclipse manual). Search past last
* quote because slashes can appear in quotes in filenames etc, but
* will always be quoted.
*/
const auto quote = std::find( rec.rbegin(), rec.rend(), RawConsts::quote );
const auto begin = quote == rec.rend() ? rec.begin() : (quote + 1).base();
return std::find( begin, rec.end(), RawConsts::slash );
}
static inline const char* first_nonspace (
const char* begin,
const char* end ) {
static inline std::string::const_iterator first_nonspace (
std::string::const_iterator begin,
std::string::const_iterator end ) {
return std::find_if_not( begin, end, RawConsts::is_separator );
}
static std::deque< string_view > splitSingleRecordString( const std::string& rec ) {
static std::deque< string_view > splitSingleRecordString( const string_view& record ) {
std::deque< string_view > dst;
string_view record( rec );
@@ -84,13 +52,6 @@ namespace Opm {
auto quote_end = std::find( current + 1, record.end(), RawConsts::quote ) + 1;
dst.push_back( { current, quote_end } );
current = quote_end;
} else if( *current == RawConsts::slash ) {
/* some records are break the optimistic algorithm of
* findTerminatingSlash and contain multiple trailing slashes
* with nothing inbetween. The first occuring one is the actual
* terminator and we simply ignore everything following it.
*/
break;
} else {
auto token_end = std::find_if( current, record.end(), RawConsts::is_separator );
dst.push_back( { current, token_end } );
@@ -111,23 +72,15 @@ namespace Opm {
*
*/
static inline bool even_quotes( const std::string& str ) {
template< typename T >
static inline bool even_quotes( const T& str ) {
return std::count( str.begin(), str.end(), RawConsts::quote ) % 2 == 0;
}
static inline std::string&& trim_record( std::string& str ) {
auto end = findTerminatingSlash( str );
/* type hack to work around findTerminatingSlash returning a const_iterator */
std::string::iterator mut_end( str.begin() );
std::advance( mut_end, std::distance< decltype( end ) >( mut_end, end ) );
str.erase( mut_end, str.end() );
return std::move( str );
}
RawRecord::RawRecord(std::string&& singleRecordString,
RawRecord::RawRecord(const string_view& singleRecordString,
const std::string& fileName,
const std::string& keywordName) :
m_sanitizedRecordString( trim_record( singleRecordString ) ),
m_sanitizedRecordString( singleRecordString ),
m_recordItems( splitSingleRecordString( m_sanitizedRecordString ) ),
m_fileName(fileName),
m_keywordName(keywordName)
@@ -136,7 +89,7 @@ namespace Opm {
if( !even_quotes( singleRecordString ) )
throw std::invalid_argument(
"Input string is not a complete record string, "
"offending string: " + singleRecordString
"offending string: '" + singleRecordString + "'"
);
}
@@ -163,14 +116,15 @@ namespace Opm {
std::cout << std::endl;
}
const std::string& RawRecord::getRecordString() const {
return m_sanitizedRecordString;
std::string RawRecord::getRecordString() const {
return m_sanitizedRecordString.string();
}
bool RawRecord::isTerminatedRecordString(const std::string& candidateRecordString) {
const auto terminatingSlash = findTerminatingSlash(candidateRecordString);
bool hasTerminatingSlash = terminatingSlash != candidateRecordString.end();
return hasTerminatingSlash && even_quotes( candidateRecordString );
bool RawRecord::isTerminatedRecordString( const string_view& str ) {
return str.back() == RawConsts::slash;
}
bool RawRecord::isTerminatedRecordString( const std::string& str ) {
return isTerminatedRecordString( string_view( str ) );
}
}

View File

@@ -35,23 +35,24 @@ namespace Opm {
class RawRecord {
public:
RawRecord(std::string&&, const std::string& fileName = "", const std::string& keywordName = "");
RawRecord( const string_view&, const std::string& fileName = "", const std::string& keywordName = "");
inline string_view pop_front();
void push_front(std::string token);
inline size_t size() const;
const std::string& getRecordString() const;
std::string getRecordString() const;
inline string_view getItem(size_t index) const;
const std::string& getFileName() const;
const std::string& getKeywordName() const;
static bool isTerminatedRecordString(const std::string& candidateRecordString);
static bool isTerminatedRecordString( const string_view& );
static bool isTerminatedRecordString( const std::string& );
void dump() const;
private:
std::string m_sanitizedRecordString;
string_view m_sanitizedRecordString;
std::deque< string_view > m_recordItems;
std::list< std::string > expanded_items;
const std::string m_fileName;

View File

@@ -54,6 +54,7 @@ namespace Opm {
inline string_view( const_iterator, size_t );
inline string_view( const std::string& );
inline string_view( const std::string&, size_t );
inline string_view( const char* );
inline const_iterator begin() const;
inline const_iterator end() const;
@@ -166,6 +167,10 @@ namespace Opm {
string_view( str.data(), count )
{}
inline string_view::string_view( const char* str ) :
string_view( str, str + std::strlen( str ) + 1 )
{}
inline string_view::const_iterator string_view::begin() const {
return this->fst;
}