/**
* Relational pipes
* Copyright © 2021 František Kučera (Frantovo.cz, GlobalCode.info)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstring>
#include <cstdint>
namespace relpipe {
namespace in {
namespace asn1 {
namespace lib {
class AbstractParserImpl;
/**
* Event-driven parser that consumes sequence of byte buffers (chunked stream)
* and produces some output (implementation specific).
*
* The flow is controlled from outside: incomming data pieces are passed to the parser through the write() method as they come.
*
* From the child class perspective (implementation of particular parser)
* data pieces are obtained through the read() method from the internal buffer maintained by AbstractParser.
*/
class AbstractParser {
private:
friend AbstractParserImpl;
AbstractParserImpl* implementation;
void rollback();
public:
virtual ~AbstractParser();
/**
* Submit a part of incomming data to the parser.
* When possible, such data are usually processed synchronously during this method call.
* If not (incomplete TLV/AVP/PDU/token/etc.) this part is appended to the internal buffer maintained by AbstractParser
* and processed in some next cycle or – in worst case – during the close() call.
*
* @param buffer readable memory
* @param length size of the buffer
*/
void write(const uint8_t* buffer, const size_t length);
/**
* Finalize the parsing process.
* After calling this method, all data from AbstractParser buffers should be consumed, parsed and results published.
* No write() call is expected after close() and would fail.
* However the parser object remains valid and may be used to get some auxiliary information (if supperted by given implementation).
*/
virtual void close();
protected:
AbstractParser();
/**
* May be thrown from the update() method in order to cancel currenty cycle and do explicit rollback.
* Same data will be processed in the next cycle.
*/
class ExplicitRollbackException {
// TODO: common super-class for exceptions, hidden implementation
};
/**
* May be called from the update() method in order to explicitly confirm that read data was successfully processed.
* Such data will not be processed again during the next cycle (even if ReadBufferUnderflowException occurs later in this cycle).
* Explicit commit() call is useful when we did some demanding work (so we are not willing to do it again in case of ReadBufferUnderflowException);
* and is necessary when we already published some outputs or did other non-idempotent operation or caused some other significant side effects.
* If there is no commit() called and update() just finishes, commit() is called implicitly.
*
* Note: There is no accessible rollback() method – throw ExplicitRollbackException instead.
*/
void commit();
/**
* Fill the buffer with incomming data of given length (exactly).
*
* If there are not enough data available, ReadBufferUnderflowException is thrown.
* This exception should not be caught in the child class – it should propagate back to the AbstractParser
* where it causes rollback(). In such case, the same data will be available during next update() cycle.
*
* @param buffer writable memory
* @param length size of the buffer
*/
void read(uint8_t* buffer, const size_t length);
/**
* Like read(), but does not update the marks (buffer positions), so it can be called again and again with same result.
*
* @param buffer writable memory
* @param length size of the buffer
*/
void peek(uint8_t* buffer, const size_t length);
/**
* @return total number of bytes that were successfully read
*/
size_t getBytesRead();
/**
* Reads input from buffers using read(), parses data and usually emits the result (e.g. to a handler/listener).
* Is specific for particular format/parser.
*
* This method is called at least once at the end of the stream (with whole stream content in read buffers).
* Usually it is called more often, by default: after each write (with just already received data part in read buffers).
*/
virtual void update() = 0;
};
}
}
}
}