decoder.hpp 6.07 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
7
    the terms of the GNU Lesser General Public License as published by
Martin Sustrik's avatar
Martin Sustrik committed
8 9 10 11 12 13
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ 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
14
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
15

16
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
17 18 19
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

Martin Sustrik's avatar
Martin Sustrik committed
20 21
#ifndef __ZMQ_DECODER_HPP_INCLUDED__
#define __ZMQ_DECODER_HPP_INCLUDED__
Martin Sustrik's avatar
Martin Sustrik committed
22 23 24

#include <stddef.h>
#include <string.h>
25
#include <stdlib.h>
Martin Sustrik's avatar
Martin Sustrik committed
26 27
#include <algorithm>

28
#include "err.hpp"
29
#include "msg.hpp"
30
#include "i_decoder.hpp"
31
#include "stdint.hpp"
32

Martin Sustrik's avatar
Martin Sustrik committed
33
namespace zmq
Martin Sustrik's avatar
Martin Sustrik committed
34 35 36
{
    //  Helper base class for decoders that know the amount of data to read
    //  in advance at any moment. Knowing the amount in advance is a property
37 38 39 40 41
    //  of the protocol used. 0MQ framing protocol is based size-prefixed
    //  paradigm, whixh qualifies it to be parsed by this class.
    //  On the other hand, XML-based transports (like XMPP or SOAP) don't allow
    //  for knowing the size of data to read in advance and should use different
    //  decoding algorithms.
Martin Sustrik's avatar
Martin Sustrik committed
42
    //
43
    //  This class implements the state machine that parses the incoming buffer.
Martin Sustrik's avatar
Martin Sustrik committed
44 45
    //  Derived class should implement individual state machine actions.

46
    template <typename T> class decoder_base_t : public i_decoder
Martin Sustrik's avatar
Martin Sustrik committed
47 48 49
    {
    public:

50
        inline decoder_base_t (size_t bufsize_) :
Martin Hurton's avatar
Martin Hurton committed
51
            next (NULL),
52
            read_pos (NULL),
Martin Sustrik's avatar
Martin Sustrik committed
53
            to_read (0),
54 55 56
            bufsize (bufsize_)
        {
            buf = (unsigned char*) malloc (bufsize_);
57
            alloc_assert (buf);
58 59
        }

60 61
        //  The destructor doesn't have to be virtual. It is mad virtual
        //  just to keep ICC and code checking tools from complaining.
62
        inline virtual ~decoder_base_t ()
63 64 65 66 67 68
        {
            free (buf);
        }

        //  Returns a buffer to be filled with binary data.
        inline void get_buffer (unsigned char **data_, size_t *size_)
Martin Sustrik's avatar
Martin Sustrik committed
69
        {
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
            //  If we are expected to read large message, we'll opt for zero-
            //  copy, i.e. we'll ask caller to fill the data directly to the
            //  message. Note that subsequent read(s) are non-blocking, thus
            //  each single read reads at most SO_RCVBUF bytes at once not
            //  depending on how large is the chunk returned from here.
            //  As a consequence, large messages being received won't block
            //  other engines running in the same I/O thread for excessive
            //  amounts of time.
            if (to_read >= bufsize) {
                *data_ = read_pos;
                *size_ = to_read;
                return;
            }

            *data_ = buf;
            *size_ = bufsize;
Martin Sustrik's avatar
Martin Sustrik committed
86 87
        }

88 89
        //  Processes the data in the buffer previously allocated using
        //  get_buffer function. size_ argument specifies nemuber of bytes
90 91 92 93 94 95
        //  actually filled into the buffer. Function returns 1 when the
        //  whole message was decoded or 0 when more data is required.
        //  On error, -1 is returned and errno set accordingly.
        //  Number of bytes processed is returned in byts_used_.
        inline int decode (const unsigned char *data_, size_t size_,
                           size_t &bytes_used_)
Martin Sustrik's avatar
Martin Sustrik committed
96
        {
97
            bytes_used_ = 0;
98

99 100 101 102
            //  In case of zero-copy simply adjust the pointers, no copying
            //  is required. Also, run the state machine in case all the data
            //  were processed.
            if (data_ == read_pos) {
103
                zmq_assert (size_ <= to_read);
104 105
                read_pos += size_;
                to_read -= size_;
106
                bytes_used_ = size_;
107

108
                while (!to_read) {
109 110 111
                    const int rc = (static_cast <T*> (this)->*next) ();
                    if (rc != 0)
                        return rc;
112
                }
113
                return 0;
114 115
            }

116
            while (bytes_used_ < size_) {
117
                //  Copy the data from buffer to the message.
118 119
                const size_t to_copy = std::min (to_read, size_ - bytes_used_);
                memcpy (read_pos, data_ + bytes_used_, to_copy);
120 121
                read_pos += to_copy;
                to_read -= to_copy;
122 123 124 125 126 127 128
                bytes_used_ += to_copy;
                //  Try to get more space in the message to fill in.
                //  If none is available, return.
                while (to_read == 0) {
                    const int rc = (static_cast <T*> (this)->*next) ();
                    if (rc != 0)
                        return rc;
Martin Hurton's avatar
Martin Hurton committed
129 130 131
                }
            }

132
            return 0;
133 134
        }

Martin Sustrik's avatar
Martin Sustrik committed
135 136 137 138
    protected:

        //  Prototype of state machine action. Action should return false if
        //  it is unable to push the data to the system.
139
        typedef int (T::*step_t) ();
Martin Sustrik's avatar
Martin Sustrik committed
140 141 142

        //  This function should be called from derived class to read data
        //  from the buffer and schedule next state machine action.
143
        inline void next_step (void *read_pos_, size_t to_read_, step_t next_)
Martin Sustrik's avatar
Martin Sustrik committed
144
        {
145
            read_pos = (unsigned char*) read_pos_;
Martin Sustrik's avatar
Martin Sustrik committed
146 147 148 149
            to_read = to_read_;
            next = next_;
        }

Martin Hurton's avatar
Martin Hurton committed
150 151
    private:

Martin Hurton's avatar
Martin Hurton committed
152 153 154 155 156
        //  Next step. If set to NULL, it means that associated data stream
        //  is dead. Note that there can be still data in the process in such
        //  case.
        step_t next;

157
        //  Where to store the read data.
158
        unsigned char *read_pos;
159 160

        //  How much data to read before taking next step.
Martin Sustrik's avatar
Martin Sustrik committed
161
        size_t to_read;
162 163

        //  The duffer for data to decode.
164 165 166
        size_t bufsize;
        unsigned char *buf;

167
        decoder_base_t (const decoder_base_t&);
168
        const decoder_base_t &operator = (const decoder_base_t&);
169
    };
Martin Sustrik's avatar
Martin Sustrik committed
170 171 172
}

#endif
173