// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef __PARSER_BASE_H__1106fa38 #define __PARSER_BASE_H__1106fa38 #include "DataTypes.h" #include #include #define BUFFERED_RECEIVER_F_FIRST 0x01 #define BUFFERED_RECEIVER_F_LAST 0x02 #define BUFFERED_RECEIVER_F_BOTH (BUFFERED_RECEIVER_F_FIRST | BUFFERED_RECEIVER_F_LAST) #define BUFFERED_RECEIVER_F_UNNAMED 0x04 #define BUFFERED_RECEIVER_F_MIDDLE 0x00 #if (DISTRO_centos6) // pre c++11 compiler doesn' support the "final" keyword #define final #else // c++11 and beyond #define final final #endif // Interface for receiver classes that accept full key/value pairs struct IParserReceiver { virtual int onKv(const char *k, size_t k_len, const char *v, size_t v_len, int flags, size_t parser_depth) = 0; }; struct IParserReceiver2 { virtual void onKvt(const char *k, size_t k_len, const char *v, size_t v_len, const DataType &type) = 0; virtual void onStartMap() = 0; virtual void onMapKey(const char *k, size_t k_len) = 0; virtual void onEndMap() = 0; virtual void onStartArray() = 0; virtual void onEndArray() = 0; virtual void onEndOfData() = 0; }; // Interface for receiver classes that can accept not only full key/value pairs, but also partial content // Senders could do multiple calls to onKey() and onValue(), followed by call to onKvDone() that signals // that both key and value data is ready. // Alternatively, when they can, senders would do single call onKv(), bringing whole data in a single buffer, // which is normally faster because this way senders could avoid unnecessary memory copying. struct IParserStreamReceiver : public IParserReceiver { virtual int onKey(const char *k, size_t k_len) = 0; virtual int onValue(const char *v, size_t v_len) = 0; virtual int onKvDone() = 0; virtual void clear() = 0; }; // This class acts as an adapter between senders that require IParserStreamReceiver and receivers // that can only accept IParserReceiver (and do not want to cope with buffering). // When onKv is received by an instance of BuferedReceiver -it will be transparently forwarded to destination // (without memory copying). // However, if BufferedReceiver instance accepts onKey, onValue calls, it buffers the data until onKvDone // is called, at which point it passes buffered data to onKv callback of the final (non stream capable) receiver. // TODO:: 1) when constructing this class, pass limits on key and value lengths as constructor parameters? // TODO:: 2) add extra callback like "onFlush()" to both IParserStreamReceiver and its implementation // BufferedReceiver, which tells BufferedReceiver that it has last chance to copy data aside // before the underlying buffer is dead. Without receiving this call, BufferedStreamReceiver // can simply collect ptr+len pairs on buffer instead of copying stuff to m_key and m_value. // Once onFlush() is received, the data must be collected from those spans, because the underlying buffer // is going to be destroyed. // Note that calls to onFlush() must be added to end of all parser functions before they loose control of their // input buffer! // However, this seems to be easy to implement: just call m_receiver.onFlush() before exiting parser's push() // method, and we finally got zero-copy! // Note that for optimization, the getAccumulatedKey() and getAccumulatedValue() // should return pointers to the input buffer. // This will in many cases cause sub-parsers to also work in zero-copy style too! class BufferedReceiver : public IParserStreamReceiver { public: BufferedReceiver(IParserReceiver &receiver, size_t parser_depth=0); virtual int onKey(const char *k, size_t k_len); virtual int onValue(const char *v, size_t v_len); virtual int onKvDone(); virtual int onKv(const char *k, size_t k_len, const char *v, size_t v_len, int flags, size_t parser_depth); virtual void clear(); // Helper methods to access accumulated key and value (read-only) const std::string &getAccumulatedKey() const { return m_key; } const std::string &getAccumulatedValue() const { return m_value; } private: IParserReceiver &m_receiver; int m_flags; // Accumulated key/value pair std::string m_key; std::string m_value; size_t m_parser_depth; }; // Base class for various streaming parsers that accept data stream in multiple pieces through // the push() calls, followed by the finish() call that signals end of the stream. // Normally, the parsers will accept data, dissect/decode it and pass resulting data as // stream of key/value pairs to a target that is either IParserReceiver or IParserStreamReceiver, class ParserBase { public: virtual ~ParserBase() {} virtual size_t push(const char *data, size_t data_len) = 0; virtual void finish() = 0; // TODO: I think this should return status of some sort, just like push() virtual const std::string &name() const = 0; virtual bool error() const = 0; virtual size_t depth() = 0; virtual void setRecursionFlag() { m_recursionFlag = true; } virtual void clearRecursionFlag() { m_recursionFlag = false; } virtual bool getRecursionFlag() const { return m_recursionFlag; } private: bool m_recursionFlag = false; }; template class BufferedParser : public ParserBase { public: template explicit BufferedParser(IParserReceiver &receiver, size_t parser_depth, _Args... _args) : m_bufferedReceiver(receiver, parser_depth), // pass any extra arguments to specific parser's constructor m_parser(m_bufferedReceiver, parser_depth, _args...) {} virtual ~BufferedParser() {} virtual size_t push(const char *data, size_t data_len) { return m_parser.push(data, data_len); } virtual void finish() { m_parser.finish(); } virtual const std::string &name() const { return m_parser.name(); } virtual bool error() const { return m_parser.error(); } virtual size_t depth() { return m_parser.depth(); } private: BufferedReceiver m_bufferedReceiver; _ParserType m_parser; }; #endif // __PARSER_BASE_H___1106fa38