#ifndef binpac_buffer_h #define binpac_buffer_h #include #include "binpac.h" namespace binpac { class FlowBuffer { public: struct Policy { int max_capacity; int min_capacity; int contract_threshold; }; enum LineBreakStyle { CR_OR_LF, // CR or LF or CRLF STRICT_CRLF, // CR followed by LF CR_LF_NUL, // CR or LF or CR-LF or CR-NUL LINE_BREAKER, // User specified linebreaker }; FlowBuffer(LineBreakStyle linebreak_style = CR_OR_LF); virtual ~FlowBuffer(); void NewData(const_byteptr begin, const_byteptr end); void NewGap(int length); // Interface for delayed parsing. Sometimes BinPAC doesn't get the // buffering right and then one can use these to feed parts // individually and assemble them internally. After calling // FinishBuffer(), one can send the uppper-layer flow an FlowEOF() to // trigger parsing. void BufferData(const_byteptr data, const_byteptr end); void FinishBuffer(); // Discard unprocessed data void DiscardData(); // Whether there is enough data for the frame bool ready() const { return message_complete_ || mode_ == UNKNOWN_MODE; } inline const_byteptr begin() const { BINPAC_ASSERT(ready()); return (buffer_n_ == 0) ? orig_data_begin_ : buffer_; } inline const_byteptr end() const { BINPAC_ASSERT(ready()); if ( buffer_n_ == 0 ) { BINPAC_ASSERT(frame_length_ >= 0); const_byteptr end = orig_data_begin_ + frame_length_; BINPAC_ASSERT(end <= orig_data_end_); return end; } else return buffer_ + buffer_n_; } inline int data_length() const { if ( buffer_n_ > 0 ) return buffer_n_; if ( frame_length_ < 0 || orig_data_begin_ + frame_length_ > orig_data_end_ ) return orig_data_end_ - orig_data_begin_; else return frame_length_; } inline bool data_available() const { return buffer_n_ > 0 || orig_data_end_ > orig_data_begin_; } void SetLineBreaker(unsigned char* lbreaker); void UnsetLineBreaker(); void NewLine(); // A negative frame_length represents a frame till EOF void NewFrame(int frame_length, bool chunked_); void GrowFrame(int new_frame_length); int data_seq() const { int data_seq_at_orig_data_begin = data_seq_at_orig_data_end_ - (orig_data_end_ - orig_data_begin_); if ( buffer_n_ > 0 ) return data_seq_at_orig_data_begin; else return data_seq_at_orig_data_begin + data_length(); } bool eof() const { return eof_; } void set_eof(); bool have_pending_request() const { return have_pending_request_; } static void init(Policy p) { policy = p; } protected: // Reset the buffer for a new message void NewMessage(); void ClearPreviousData(); // Expand the buffer to at least bytes. If there // are contents in the existing buffer, copy them to the new // buffer. void ExpandBuffer(int length); // Contract the buffer to some minimum capacity. // Existing contents in the buffer are preserved (but only usage // at the time of creation this function is when the contents // are being discarded due to parsing exception or have already been // copied out after parsing a complete unit). void ContractBuffer(); // Reset line state when transit from frame mode to line mode. void ResetLineState(); void AppendToBuffer(const_byteptr data, int len); // MarkOrCopy{Line,Frame} sets message_complete_ and // marks begin/end pointers if a line/frame is complete, // otherwise it clears message_complete_ and copies all // the original data to the buffer. // void MarkOrCopy(); void MarkOrCopyLine(); void MarkOrCopyFrame(); void MarkOrCopyLine_CR_OR_LF(); void MarkOrCopyLine_STRICT_CRLF(); void MarkOrCopyLine_LINEBREAK(); int buffer_n_; // number of bytes in the buffer int buffer_length_; // size of the buffer unsigned char* buffer_; bool message_complete_; int frame_length_; bool chunked_; const_byteptr orig_data_begin_, orig_data_end_; LineBreakStyle linebreak_style_; LineBreakStyle linebreak_style_default; unsigned char linebreaker_; enum { UNKNOWN_MODE, LINE_MODE, FRAME_MODE, } mode_; enum { CR_OR_LF_0, CR_OR_LF_1, STRICT_CRLF_0, STRICT_CRLF_1, FRAME_0, } state_; int data_seq_at_orig_data_end_; bool eof_; bool have_pending_request_; static Policy policy; }; typedef FlowBuffer* flow_buffer_t; } // namespace binpac #endif // binpac_buffer_h