1/*
2 Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30#ifndef __ZMQ_ENCODER_HPP_INCLUDED__
31#define __ZMQ_ENCODER_HPP_INCLUDED__
32
33#if defined(_MSC_VER)
34#ifndef NOMINMAX
35#define NOMINMAX
36#endif
37#endif
38
39#include <stddef.h>
40#include <string.h>
41#include <stdlib.h>
42#include <algorithm>
43
44#include "err.hpp"
45#include "i_encoder.hpp"
46#include "msg.hpp"
47
48namespace zmq
49{
50// Helper base class for encoders. It implements the state machine that
51// fills the outgoing buffer. Derived classes should implement individual
52// state machine actions.
53
54template <typename T> class encoder_base_t : public i_encoder
55{
56 public:
57 inline explicit encoder_base_t (size_t bufsize_) :
58 _write_pos (0),
59 _to_write (0),
60 _next (NULL),
61 _new_msg_flag (false),
62 _buf_size (bufsize_),
63 _buf (static_cast<unsigned char *> (malloc (bufsize_))),
64 _in_progress (NULL)
65 {
66 alloc_assert (_buf);
67 }
68
69 // The destructor doesn't have to be virtual. It is made virtual
70 // just to keep ICC and code checking tools from complaining.
71 inline virtual ~encoder_base_t () { free (_buf); }
72
73 // The function returns a batch of binary data. The data
74 // are filled to a supplied buffer. If no buffer is supplied (data_
75 // points to NULL) decoder object will provide buffer of its own.
76 inline size_t encode (unsigned char **data_, size_t size_)
77 {
78 unsigned char *buffer = !*data_ ? _buf : *data_;
79 size_t buffersize = !*data_ ? _buf_size : size_;
80
81 if (in_progress () == NULL)
82 return 0;
83
84 size_t pos = 0;
85 while (pos < buffersize) {
86 // If there are no more data to return, run the state machine.
87 // If there are still no data, return what we already have
88 // in the buffer.
89 if (!_to_write) {
90 if (_new_msg_flag) {
91 int rc = _in_progress->close ();
92 errno_assert (rc == 0);
93 rc = _in_progress->init ();
94 errno_assert (rc == 0);
95 _in_progress = NULL;
96 break;
97 }
98 (static_cast<T *> (this)->*_next) ();
99 }
100
101 // If there are no data in the buffer yet and we are able to
102 // fill whole buffer in a single go, let's use zero-copy.
103 // There's no disadvantage to it as we cannot stuck multiple
104 // messages into the buffer anyway. Note that subsequent
105 // write(s) are non-blocking, thus each single write writes
106 // at most SO_SNDBUF bytes at once not depending on how large
107 // is the chunk returned from here.
108 // As a consequence, large messages being sent won't block
109 // other engines running in the same I/O thread for excessive
110 // amounts of time.
111 if (!pos && !*data_ && _to_write >= buffersize) {
112 *data_ = _write_pos;
113 pos = _to_write;
114 _write_pos = NULL;
115 _to_write = 0;
116 return pos;
117 }
118
119 // Copy data to the buffer. If the buffer is full, return.
120 size_t to_copy = std::min (_to_write, buffersize - pos);
121 memcpy (buffer + pos, _write_pos, to_copy);
122 pos += to_copy;
123 _write_pos += to_copy;
124 _to_write -= to_copy;
125 }
126
127 *data_ = buffer;
128 return pos;
129 }
130
131 void load_msg (msg_t *msg_)
132 {
133 zmq_assert (in_progress () == NULL);
134 _in_progress = msg_;
135 (static_cast<T *> (this)->*_next) ();
136 }
137
138 protected:
139 // Prototype of state machine action.
140 typedef void (T::*step_t) ();
141
142 // This function should be called from derived class to write the data
143 // to the buffer and schedule next state machine action.
144 inline void next_step (void *write_pos_,
145 size_t to_write_,
146 step_t next_,
147 bool new_msg_flag_)
148 {
149 _write_pos = static_cast<unsigned char *> (write_pos_);
150 _to_write = to_write_;
151 _next = next_;
152 _new_msg_flag = new_msg_flag_;
153 }
154
155 msg_t *in_progress () { return _in_progress; }
156
157 private:
158 // Where to get the data to write from.
159 unsigned char *_write_pos;
160
161 // How much data to write before next step should be executed.
162 size_t _to_write;
163
164 // Next step. If set to NULL, it means that associated data stream
165 // is dead.
166 step_t _next;
167
168 bool _new_msg_flag;
169
170 // The buffer for encoded data.
171 const size_t _buf_size;
172 unsigned char *const _buf;
173
174 msg_t *_in_progress;
175
176 ZMQ_NON_COPYABLE_NOR_MOVABLE (encoder_base_t)
177};
178}
179
180#endif
181