Commit 1705b77f authored by Simon Morlat's avatar Simon Morlat

Add in tester an example of an API that uses inheritance, with a base class...

Add in tester an example of an API that uses inheritance, with a base class used to represent several kind of different elements.
Remove a useless usage shared_ptr<> in the Parser and Regognizers. This should increase a bit the parsing performance.
parent 21872080
......@@ -68,7 +68,7 @@ public:
void setName(const std::string &name);
const std::string &getName()const;
BELR_PUBLIC size_t feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos);
BELR_PUBLIC size_t feed(ParserContextBase &ctx, const std::string &input, size_t pos);
unsigned int getId()const{
return mId;
}
......@@ -84,7 +84,7 @@ protected:
/*returns true if the transition map is complete, false otherwise*/
virtual bool _getTransitionMap(TransitionMap *mask);
virtual void _optimize(int recursionLevel)=0;
virtual size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) = 0;
virtual size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) = 0;
std::string mName;
unsigned int mId = 0;
......@@ -108,7 +108,7 @@ public:
CharRecognizer(int to_recognize, bool caseSensitive=false);
CharRecognizer(BinaryGrammarBuilder &istr);
private:
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
void _optimize(int recursionLevel) override;
virtual void _serialize(BinaryOutputStream &fstr) override;
......@@ -123,11 +123,11 @@ public:
Selector(BinaryGrammarBuilder &istr);
protected:
void _optimize(int recursionLevel) override;
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
bool _getTransitionMap(TransitionMap *mask) override;
virtual void _serialize(BinaryOutputStream &fstr) override;
size_t _feedExclusive(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos);
size_t _feedExclusive(ParserContextBase &ctx, const std::string &input, size_t pos);
std::list<std::shared_ptr<Recognizer>> mElements;
bool mIsExclusive = false;
......@@ -139,7 +139,7 @@ public:
ExclusiveSelector();
ExclusiveSelector(BinaryGrammarBuilder &istr);
private:
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
};
class Sequence : public Recognizer{
......@@ -154,7 +154,7 @@ protected:
void _optimize(int recursionLevel) override;
private:
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
std::list<std::shared_ptr<Recognizer>> mElements;
};
......@@ -171,7 +171,7 @@ protected:
void _optimize(int recursionLevel) override;
private:
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
std::shared_ptr<Recognizer> mRecognizer;
int mMin = 0;
......@@ -195,7 +195,7 @@ public:
private:
virtual void _serialize(BinaryOutputStream &fstr) override;
void _optimize(int recursionLevel) override;
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
int mBegin;
int mEnd;
......@@ -210,7 +210,7 @@ public:
private:
void _optimize(int recursionLevel) override;
virtual void _serialize(BinaryOutputStream &fstr) override;
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
std::string mLiteral;
size_t mLiteralSize;
......@@ -237,7 +237,7 @@ public:
private:
void _optimize(int recursionLevel) override;
virtual void _serialize(BinaryOutputStream &fstr) override;
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
std::shared_ptr<Recognizer> mRecognizer;
};
......@@ -259,7 +259,7 @@ public:
private:
void _optimize(int recursionLevel) override;
virtual void _serialize(BinaryOutputStream &fstr) override;
size_t _feed(const std::shared_ptr<ParserContextBase> &ctx, const std::string &input, size_t pos) override;
size_t _feed(ParserContextBase &ctx, const std::string &input, size_t pos) override;
std::shared_ptr<Recognizer> mRecognizer;
};
......
......@@ -46,6 +46,12 @@ inline T universal_pointer_cast(const std::shared_ptr<U>& sp){
return std::static_pointer_cast<typename T::element_type>(sp);
}
//template <class T, class U>
//inline std::shared_ptr<T> universal_pointer_cast(const std::shared_ptr<U>& sp){
// return std::static_pointer_cast<T>(sp);
//}
template <class T, class U>
inline T universal_pointer_cast(U * p){
return static_cast<T>(p);
......@@ -85,7 +91,9 @@ private:
inline void _invokeWithChild(_parserElementT obj, typename std::enable_if<std::is_floating_point<_valueT>::value, _parserElementT>::type child){
}
template <typename _valueT>
inline void _invokeWithChild(_parserElementT obj, typename std::enable_if<std::is_convertible<_valueT, _parserElementT>::value, _parserElementT>::type child){
inline void _invokeWithChild(
typename std::enable_if< std::is_convertible<typename _functorT::first_argument_type, _parserElementT>::value, _parserElementT>::type obj,
typename std::enable_if< std::is_convertible<_valueT, _parserElementT>::value, _parserElementT>::type child ){
mFunc(universal_pointer_cast<typename std::remove_reference<typename _functorT::first_argument_type>::type>(obj),
universal_pointer_cast<typename std::remove_reference<typename _functorT::second_argument_type>::type>(child));
}
......@@ -126,6 +134,7 @@ private:
std::shared_ptr<HandlerContext<_parserElementT>> mCachedContext;
};
template <typename _createElementFn, typename _parserElementT>
class ParserHandler : public ParserHandlerBase<_parserElementT>{
public:
......@@ -147,7 +156,6 @@ private:
}
template <typename _funcT>
typename std::enable_if<std::is_convertible<_funcT, std::function<_derivedParserElementT()>>::value, _derivedParserElementT>::type _invoke(const std::string &value, size_t begin, size_t count){
// Case where the create func accepts two strings for rulename and matched characters.
return mHandlerCreateFunc();
}
_createElementFn mHandlerCreateFunc;
......@@ -293,11 +301,11 @@ public:
BELR_PUBLIC std::shared_ptr<DebugElement> parseInput(const std::string &rulename, const std::string &input, size_t *parsed_size);
};
//Utility functions for handlers/collectors objects instantiation and properties accessors
template <typename _retT>
inline std::function< std::shared_ptr<_retT> ()> make_fn() {
return std::bind(&std::make_shared<_retT>);
}
//
// Utility functions for handlers/collectors objects instantiation and properties accessors
//
/* For parser relying on raw pointers */
template <typename _retT>
inline std::function< _retT ()> make_fn(_retT (*arg)()){
......@@ -329,6 +337,13 @@ inline std::function< void (_klassT*,_argT)> make_fn(void (_klassT::*arg)(_argT)
return std::function< void (_klassT*,_argT)>(std::mem_fn(arg));
}
/* For parsers using shared_ptr<> */
template <typename _retT>
inline std::function< std::shared_ptr<_retT> ()> make_fn() {
return std::bind(&std::make_shared<_retT>);
}
template <typename _klassT, typename _argT>
inline std::function< void (std::shared_ptr<_klassT>,_argT)> make_sfn(void (_klassT::*arg)(_argT)){
return std::function< void (std::shared_ptr<_klassT>,_argT)>(std::mem_fn(arg));
......@@ -487,7 +502,7 @@ inline void ParserContext<_parserElementT>::_beginParse(ParserLocalContext & lct
if (mHandlerStack.empty()){
fatal("Cannot parse when mHandlerStack is empty. You must define a top-level rule handler.");
}
lctx.set(ctx,rec,mHandlerStack.back()->getLastIterator());
lctx.set(ctx, rec, mHandlerStack.back()->getLastIterator());
}
template <typename _parserElementT>
......@@ -615,14 +630,21 @@ template <typename _parserElementT>
_parserElementT Parser<_parserElementT>::parseInput(const std::string &rulename, const std::string &input, size_t *parsed_size){
size_t parsed;
std::shared_ptr<Recognizer> rec=mGrammar->getRule(rulename);
auto pctx=std::make_shared<ParserContext<_parserElementT>>(*this);
ParserContext<_parserElementT> pctx(*this);
auto h=getHandler(rec->getId());
if (!h){
std::ostringstream str;
str<<"There is no handler for rule '"<<rulename<<"'.";
fatal(str.str().c_str());
}
//auto t_start = std::chrono::high_resolution_clock::now();
parsed=rec->feed(pctx, input, 0);
//auto t_end = std::chrono::high_resolution_clock::now();
//cout<<"Recognition done in "<<std::chrono::duration<double, std::milli>(t_end-t_start).count()<<" milliseconds"<<std::endl;
if (parsed_size) *parsed_size=parsed;
auto ret= pctx->createRootObject(input, parsed);
auto ret= pctx.createRootObject(input, parsed);
return ret;
}
}
......
......@@ -30,6 +30,24 @@ void fatal(const char *message){
bctbx_fatal("%s", message);
}
/* Dummy ParserContext, actually used for optimization phase, to let invoke the feed() function without
* instanciating anything.*/
class DummyParserContext : public ParserContextBase{
public:
virtual void beginParse(ParserLocalContext &ctx, const std::shared_ptr<Recognizer> &rec) override{
}
virtual void endParse(const ParserLocalContext &ctx, const std::string &input, size_t begin, size_t count) override{
}
virtual std::shared_ptr<HandlerContextBase> branch() override{
return nullptr;
}
virtual void merge(const std::shared_ptr<HandlerContextBase> &other) override{
}
virtual void removeBranch(const std::shared_ptr<HandlerContextBase> &other) override{
}
};
// =============================================================================
TransitionMap::TransitionMap(){
......@@ -170,7 +188,7 @@ const string &Recognizer::getName()const{
return mName;
}
size_t Recognizer::feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t Recognizer::feed(ParserContextBase &ctx, const string &input, size_t pos){
size_t match;
#ifdef BELR_DEBUG
......@@ -178,7 +196,7 @@ size_t Recognizer::feed(const shared_ptr<ParserContextBase> &ctx, const string &
#endif
ParserLocalContext hctx;
if (ctx) ctx->beginParse(hctx, shared_from_this());
ctx.beginParse(hctx, shared_from_this());
match=_feed(ctx, input, pos);
if (match!=string::npos && match>0){
#ifdef BELR_DEBUG
......@@ -188,7 +206,7 @@ size_t Recognizer::feed(const shared_ptr<ParserContextBase> &ctx, const string &
}
#endif
}
if (ctx) ctx->endParse(hctx, input, pos, match);
ctx.endParse(hctx, input, pos, match);
return match;
}
......@@ -212,7 +230,8 @@ bool Recognizer::_getTransitionMap(TransitionMap* mask){
input.resize(2,'\0');
for(int i=0;i<256;++i){
input[0]=i;
if (feed(nullptr,input,0)==1)
DummyParserContext pctx;
if (feed(pctx,input,0)==1)
mask->mPossibleChars[i]=true;
}
return true;
......@@ -237,7 +256,7 @@ CharRecognizer::CharRecognizer(int to_recognize, bool caseSensitive) : mToRecogn
}
}
size_t CharRecognizer::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t CharRecognizer::_feed(ParserContextBase &ctx, const string &input, size_t pos){
int c = (unsigned char)input[pos];
if (mCaseSensitive){
return c == mToRecognize ? 1 : string::npos;
......@@ -278,7 +297,7 @@ bool Selector::_getTransitionMap(TransitionMap* mask){
return true;
}
size_t Selector::_feedExclusive(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t Selector::_feedExclusive(ParserContextBase &ctx, const string &input, size_t pos){
size_t matched=0;
for (auto it=mElements.begin(); it!=mElements.end(); ++it){
......@@ -290,7 +309,7 @@ size_t Selector::_feedExclusive(const shared_ptr<ParserContextBase> &ctx, const
return string::npos;
}
size_t Selector::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t Selector::_feed(ParserContextBase &ctx, const string &input, size_t pos){
if (mIsExclusive) return _feedExclusive(ctx, input, pos);
size_t matched=0;
......@@ -299,20 +318,19 @@ size_t Selector::_feed(const shared_ptr<ParserContextBase> &ctx, const string &i
for (auto it=mElements.begin(); it!=mElements.end(); ++it){
shared_ptr<HandlerContextBase> br;
if (ctx) br=ctx->branch();
br = ctx.branch();
matched=(*it)->feed(ctx, input, pos);
if (matched!=string::npos && matched>bestmatch) {
bestmatch=matched;
if (bestBranch) ctx->removeBranch(bestBranch);
if (bestBranch) ctx.removeBranch(bestBranch);
bestBranch=br;
}else{
if (ctx)
ctx->removeBranch(br);
ctx.removeBranch(br);
}
}
if (bestmatch==0) return string::npos;
if (ctx && bestmatch!=string::npos){
ctx->merge(bestBranch);
if (bestmatch!=string::npos){
ctx.merge(bestBranch);
}
return bestmatch;
}
......@@ -341,6 +359,11 @@ Selector::Selector(BinaryGrammarBuilder& istr) : Recognizer(istr){
}
/* The purpose of the optimization is to determine if a selector is exclusive, ie that only a single branch can match.
* This is done by examining if the character transitions to jump to each branch (sub-recognizers) do intersect.
* If no, it means that the first branch that will match during the parsing will necessarily be the good one, so that there is no need to
* examine others.
*/
void Selector::_optimize(int recursionLevel){
for (auto it=mElements.begin(); it!=mElements.end(); ++it){
(*it)->optimize(recursionLevel);
......@@ -373,7 +396,7 @@ ExclusiveSelector::ExclusiveSelector() : Selector(true){
ExclusiveSelector::ExclusiveSelector(BinaryGrammarBuilder &istr) : Selector(istr){
}
size_t ExclusiveSelector::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t ExclusiveSelector::_feed(ParserContextBase &ctx, const string &input, size_t pos){
return Selector::_feedExclusive(ctx, input, pos);
}
......@@ -395,7 +418,7 @@ bool Sequence::_getTransitionMap(TransitionMap* mask){
}
size_t Sequence::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t Sequence::_feed(ParserContextBase &ctx, const string &input, size_t pos){
size_t matched=0;
size_t total=0;
......@@ -442,7 +465,7 @@ shared_ptr<Loop> Loop::setRecognizer(const shared_ptr<Recognizer> &element, int
return static_pointer_cast<Loop>(shared_from_this());
}
size_t Loop::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t Loop::_feed(ParserContextBase &ctx, const string &input, size_t pos){
size_t matched=0;
size_t total=0;
int repeat;
......@@ -482,7 +505,7 @@ void Loop::_optimize(int recursionLevel){
CharRange::CharRange(int begin, int end) : mBegin(begin), mEnd(end){
}
size_t CharRange::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t CharRange::_feed(ParserContextBase &ctx, const string &input, size_t pos){
int c = (unsigned char)input[pos];
if (c >= mBegin && c <= mEnd) return 1;
return string::npos;
......@@ -530,7 +553,7 @@ Literal::Literal(const string& lit) : mLiteral(tolower(lit)), mLiteralSize(mLite
}
size_t Literal::_feed(const shared_ptr< ParserContextBase >& ctx, const string& input, size_t pos){
size_t Literal::_feed(ParserContextBase &ctx, const string& input, size_t pos){
size_t i;
for(i=0;i<mLiteralSize;++i){
if (::tolower(input[pos+i])!=mLiteral[i]) return string::npos;
......@@ -569,7 +592,7 @@ shared_ptr<Recognizer> RecognizerPointer::getPointed(){
return mRecognizer;
}
size_t RecognizerPointer::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t RecognizerPointer::_feed(ParserContextBase &ctx, const string &input, size_t pos){
if (mRecognizer){
return mRecognizer->feed(ctx, input, pos);
}else{
......@@ -601,7 +624,7 @@ shared_ptr<Recognizer> RecognizerAlias::getPointed(){
return mRecognizer;
}
size_t RecognizerAlias::_feed(const shared_ptr<ParserContextBase> &ctx, const string &input, size_t pos){
size_t RecognizerAlias::_feed(ParserContextBase &ctx, const string &input, size_t pos){
if (mRecognizer){
return mRecognizer->feed(ctx, input, pos);
}else{
......
......@@ -127,10 +127,171 @@ static void parser_connected_to_c_functions(void) {
sip_response_destroy(resp);
}
//
// Parser with inheritance.
//
/* Base class for all parser elements*/
class Object{
public:
virtual ~Object() = default;
};
/* Base class for SIP headers */
class SipHeader : public Object{
public:
SipHeader(const string &headerName) : mHeaderName(headerName){}
const string &getName()const{
return mHeaderName;
}
protected:
void setHeaderName(const string &headerName){
mHeaderName = headerName;
}
private:
string mHeaderName;
};
/*
* The SipHeaderHolder is a kind of container for all possible types of SIP headers.
* This special type is needed so that it is possible to parse headers in an unitary way (not in a full message).
* It does not have to be part of the parser API. Keep it internal as an utility.
*/
class SipHeaderHolder : public Object{
public:
SipHeaderHolder(){};
void setHeader(const shared_ptr<SipHeader> &header){
mHeader = header;
}
shared_ptr<SipHeader> getHeader()const{
return mHeader;
}
private:
shared_ptr<SipHeader> mHeader;
};
/*
* SIP From
*/
class SipFrom : public SipHeader{
public:
SipFrom() : SipHeader("From"){};
void setUri(const string& uri){
mUri = uri;
}
const string &getUri()const{
return mUri;
}
private:
string mUri;
};
/*
* Extension header
*/
class ExtensionHeader : public SipHeader{
public:
ExtensionHeader() : SipHeader("generic"){};
void setName(const string &headerName){
SipHeader::setHeaderName(headerName);
}
void setValue(const string &headerValue){
mHeaderValue = headerValue;
}
const string &getValue()const{
return mHeaderValue;
}
private:
string mHeaderValue;
};
/*
* SIP request. Contains headers.
*/
class SipRequest : public Object{
public:
SipRequest(){};
void addHeader(const shared_ptr<SipHeader> &header){
mHeaders.push_back(header);
}
shared_ptr <SipHeader> getHeader(const string &name){
for (auto h : mHeaders){
if (strcasecmp(h->getName().c_str(), name.c_str()) == 0) return h;
}
return nullptr;
}
/* Hide this from the API documentation */
void addHeaderHolder(const shared_ptr<SipHeaderHolder> &holder){
if (holder->getHeader())
addHeader(holder->getHeader());
}
private:
list<shared_ptr<SipHeader>> mHeaders;
};
static void parser_with_inheritance(void){
string grammarToParse = bcTesterRes("sipgrammar.txt");
string sipmessage = openFile(bcTesterRes("register.txt"));
size_t pos = 0;
BC_ASSERT_TRUE(sipmessage.size() > 0);
ABNFGrammarBuilder builder;
//Read grammar put it in object grammar
shared_ptr<Grammar> grammar=builder.createFromAbnfFile(grammarToParse, make_shared<CoreRules>());
BC_ASSERT_FALSE(!grammar);
if (!grammar) return;
shared_ptr<Parser<shared_ptr<Object>>> parser = make_shared<Parser<shared_ptr<Object>>>(grammar);
/* The request collects headers in a generic way, thanks to the SipHeaderHolder. */
parser->setHandler("request", make_fn<SipRequest>())
->setCollector("message-header", make_sfn(&SipRequest::addHeaderHolder));
parser->setHandler("from", make_fn<SipFrom>())
->setCollector("sip-uri", make_sfn(&SipFrom::setUri));
parser->setHandler("extension-header", make_fn<ExtensionHeader>())
->setCollector("header-name", make_sfn(&ExtensionHeader::setName))
->setCollector("header-value", make_sfn(&ExtensionHeader::setValue));
/* The message-header rule (representing all sub-rules of SIP headers) is handled by the SipHeaderHolder.
* It requires explicit collectors for all sub-rules. */
parser->setHandler("message-header", make_fn<SipHeaderHolder>())
->setCollector("from", make_sfn(&SipHeaderHolder::setHeader))
->setCollector("extension-header", make_sfn(&SipHeaderHolder::setHeader));
// Parse the full message
shared_ptr<Object> elem = parser->parseInput("request", sipmessage, &pos);
BC_ASSERT_TRUE(elem != nullptr);
if (!elem) return;
BC_ASSERT_EQUAL(pos, sipmessage.size(), int, "%i");
shared_ptr<SipRequest> request = dynamic_pointer_cast<SipRequest>(elem);
BC_ASSERT_TRUE(request != nullptr);
if (!request) return;
BC_ASSERT_TRUE(request->getHeader("from") != nullptr);
BC_ASSERT_TRUE(request->getHeader("CustomHeader") != nullptr);
// Parse a single sip header
elem = parser->parseInput("message-header", "From: <sip:bob@example.net>\r\n", &pos);
BC_ASSERT_TRUE(elem != nullptr);
if (!elem) return;
auto holder = dynamic_pointer_cast<SipHeaderHolder>(elem);
BC_ASSERT_TRUE(holder != nullptr);
if (!holder) return;
BC_ASSERT_TRUE(dynamic_pointer_cast<SipFrom>(holder->getHeader()) != nullptr);
}
static test_t tests[] = {
TEST_NO_TAG("Parser connected to C functions", parser_connected_to_c_functions),
TEST_NO_TAG("Parser with inheritance", parser_with_inheritance)
};
test_suite_t parser_suite = {
......
......@@ -9,6 +9,7 @@ Supported: outbound
Contact: <sip:smorlat2@78.220.48.77:41076;transport=tls>;+sip.instance="<urn:uuid:2d839989-0bf2-49ba-80b7-2146037f729a>"
Expires: 3600
User-Agent: Linphone/3.7.0 (belle-sip/1.3.3)
CustomHeader: some-custom-value
Content-Length: 0
Authorization: Digest realm="siptest.linphone.org", nonce="f5Zb2AAAAABXkU1AAAD253sZEAsAAAAA", algorithm=MD5, opaque="+GNywA==", username="smorlat2", uri="sip:siptest.linphone.org", response="f86327204e4c826f47d761c9ad3afdb3", cnonce="faaa9595", nc=00000001, qop=auth
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment