- merge with master
[oweals/gnunet.git] / src / transport / tcp_server_mst_legacy.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file util/server_mst.c
23  * @brief convenience functions for handling inbound message buffers
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29
30
31 #if HAVE_UNALIGNED_64_ACCESS
32 #define ALIGN_FACTOR 4
33 #else
34 #define ALIGN_FACTOR 8
35 #endif
36
37
38 /**
39  * Handle to a message stream tokenizer.
40  */
41 struct GNUNET_SERVER_MessageStreamTokenizer
42 {
43
44   /**
45    * Function to call on completed messages.
46    */
47   GNUNET_SERVER_MessageTokenizerCallback cb;
48
49   /**
50    * Closure for @e cb.
51    */
52   void *cb_cls;
53
54   /**
55    * Size of the buffer (starting at @e hdr).
56    */
57   size_t curr_buf;
58
59   /**
60    * How many bytes in buffer have we already processed?
61    */
62   size_t off;
63
64   /**
65    * How many bytes in buffer are valid right now?
66    */
67   size_t pos;
68
69   /**
70    * Beginning of the buffer.  Typed like this to force alignment.
71    */
72   struct GNUNET_MessageHeader *hdr;
73
74 };
75
76
77
78 /**
79  * Create a message stream tokenizer.
80  *
81  * @param cb function to call on completed messages
82  * @param cb_cls closure for @a cb
83  * @return handle to tokenizer
84  */
85 struct GNUNET_SERVER_MessageStreamTokenizer *
86 GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb,
87                           void *cb_cls)
88 {
89   struct GNUNET_SERVER_MessageStreamTokenizer *ret;
90
91   ret = GNUNET_new (struct GNUNET_SERVER_MessageStreamTokenizer);
92   ret->hdr = GNUNET_malloc (GNUNET_MIN_MESSAGE_SIZE);
93   ret->curr_buf = GNUNET_MIN_MESSAGE_SIZE;
94   ret->cb = cb;
95   ret->cb_cls = cb_cls;
96   return ret;
97 }
98
99
100 /**
101  * Add incoming data to the receive buffer and call the
102  * callback for all complete messages.
103  *
104  * @param mst tokenizer to use
105  * @param client_identity ID of client for which this is a buffer
106  * @param buf input data to add
107  * @param size number of bytes in @a buf
108  * @param purge should any excess bytes in the buffer be discarded
109  *       (i.e. for packet-based services like UDP)
110  * @param one_shot only call callback once, keep rest of message in buffer
111  * @return #GNUNET_OK if we are done processing (need more data)
112  *         #GNUNET_NO if @a one_shot was set and we have another message ready
113  *         #GNUNET_SYSERR if the data stream is corrupt
114  */
115 int
116 GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
117                            void *client_identity,
118                            const char *buf, size_t size,
119                            int purge, int one_shot)
120 {
121   const struct GNUNET_MessageHeader *hdr;
122   size_t delta;
123   uint16_t want;
124   char *ibuf;
125   int need_align;
126   unsigned long offset;
127   int ret;
128
129   GNUNET_assert (mst->off <= mst->pos);
130   GNUNET_assert (mst->pos <= mst->curr_buf);
131   LOG (GNUNET_ERROR_TYPE_DEBUG,
132        "Server-mst receives %u bytes with %u bytes already in private buffer\n",
133        (unsigned int) size, (unsigned int) (mst->pos - mst->off));
134   ret = GNUNET_OK;
135   ibuf = (char *) mst->hdr;
136   while (mst->pos > 0)
137   {
138 do_align:
139     GNUNET_assert (mst->pos >= mst->off);
140     if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
141         (0 != (mst->off % ALIGN_FACTOR)))
142     {
143       /* need to align or need more space */
144       mst->pos -= mst->off;
145       memmove (ibuf, &ibuf[mst->off], mst->pos);
146       mst->off = 0;
147     }
148     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
149     {
150       delta =
151           GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
152                       (mst->pos - mst->off), size);
153       GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
154       mst->pos += delta;
155       buf += delta;
156       size -= delta;
157     }
158     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
159     {
160       if (purge)
161       {
162         mst->off = 0;
163         mst->pos = 0;
164       }
165       return GNUNET_OK;
166     }
167     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
168     want = ntohs (hdr->size);
169     if (want < sizeof (struct GNUNET_MessageHeader))
170     {
171       GNUNET_break_op (0);
172       return GNUNET_SYSERR;
173     }
174     if ( (mst->curr_buf - mst->off < want) &&
175          (mst->off > 0) )
176     {
177       /* can get more space by moving */
178       mst->pos -= mst->off;
179       memmove (ibuf, &ibuf[mst->off], mst->pos);
180       mst->off = 0;
181     }
182     if (mst->curr_buf < want)
183     {
184       /* need to get more space by growing buffer */
185       GNUNET_assert (0 == mst->off);
186       mst->hdr = GNUNET_realloc (mst->hdr, want);
187       ibuf = (char *) mst->hdr;
188       mst->curr_buf = want;
189     }
190     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
191     if (mst->pos - mst->off < want)
192     {
193       delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
194       GNUNET_assert (mst->pos + delta <= mst->curr_buf);
195       GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
196       mst->pos += delta;
197       buf += delta;
198       size -= delta;
199     }
200     if (mst->pos - mst->off < want)
201     {
202       if (purge)
203       {
204         mst->off = 0;
205         mst->pos = 0;
206       }
207       return GNUNET_OK;
208     }
209     if (one_shot == GNUNET_SYSERR)
210     {
211       /* cannot call callback again, but return value saying that
212        * we have another full message in the buffer */
213       ret = GNUNET_NO;
214       goto copy;
215     }
216     if (one_shot == GNUNET_YES)
217       one_shot = GNUNET_SYSERR;
218     mst->off += want;
219     if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
220       return GNUNET_SYSERR;
221     if (mst->off == mst->pos)
222     {
223       /* reset to beginning of buffer, it's free right now! */
224       mst->off = 0;
225       mst->pos = 0;
226     }
227   }
228   GNUNET_assert (0 == mst->pos);
229   while (size > 0)
230   {
231     LOG (GNUNET_ERROR_TYPE_DEBUG,
232          "Server-mst has %u bytes left in inbound buffer\n",
233          (unsigned int) size);
234     if (size < sizeof (struct GNUNET_MessageHeader))
235       break;
236     offset = (unsigned long) buf;
237     need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
238     if (GNUNET_NO == need_align)
239     {
240       /* can try to do zero-copy and process directly from original buffer */
241       hdr = (const struct GNUNET_MessageHeader *) buf;
242       want = ntohs (hdr->size);
243       if (want < sizeof (struct GNUNET_MessageHeader))
244       {
245         GNUNET_break_op (0);
246         mst->off = 0;
247         return GNUNET_SYSERR;
248       }
249       if (size < want)
250         break;                  /* or not: buffer incomplete, so copy to private buffer... */
251       if (one_shot == GNUNET_SYSERR)
252       {
253         /* cannot call callback again, but return value saying that
254          * we have another full message in the buffer */
255         ret = GNUNET_NO;
256         goto copy;
257       }
258       if (one_shot == GNUNET_YES)
259         one_shot = GNUNET_SYSERR;
260       if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
261         return GNUNET_SYSERR;
262       buf += want;
263       size -= want;
264     }
265     else
266     {
267       /* need to copy to private buffer to align;
268        * yes, we go a bit more spagetti than usual here */
269       goto do_align;
270     }
271   }
272 copy:
273   if ((size > 0) && (!purge))
274   {
275     if (size + mst->pos > mst->curr_buf)
276     {
277       mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
278       ibuf = (char *) mst->hdr;
279       mst->curr_buf = size + mst->pos;
280     }
281     GNUNET_assert (size + mst->pos <= mst->curr_buf);
282     GNUNET_memcpy (&ibuf[mst->pos], buf, size);
283     mst->pos += size;
284   }
285   if (purge)
286   {
287     mst->off = 0;
288     mst->pos = 0;
289   }
290   LOG (GNUNET_ERROR_TYPE_DEBUG,
291        "Server-mst leaves %u bytes in private buffer\n",
292        (unsigned int) (mst->pos - mst->off));
293   return ret;
294 }
295
296
297 /**
298  * Destroys a tokenizer.
299  *
300  * @param mst tokenizer to destroy
301  */
302 void
303 GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst)
304 {
305   GNUNET_free (mst->hdr);
306   GNUNET_free (mst);
307 }
308
309
310
311 /* end of server_mst.c */