ack florian
[oweals/gnunet.git] / src / util / mst.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 2016 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/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 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
38
39
40 /**
41  * Handle to a message stream tokenizer.
42  */
43 struct GNUNET_MessageStreamTokenizer
44 {
45
46   /**
47    * Function to call on completed messages.
48    */
49   GNUNET_MessageTokenizerCallback cb;
50
51   /**
52    * Closure for @e cb.
53    */
54   void *cb_cls;
55
56   /**
57    * Size of the buffer (starting at @e hdr).
58    */
59   size_t curr_buf;
60
61   /**
62    * How many bytes in buffer have we already processed?
63    */
64   size_t off;
65
66   /**
67    * How many bytes in buffer are valid right now?
68    */
69   size_t pos;
70
71   /**
72    * Beginning of the buffer.  Typed like this to force alignment.
73    */
74   struct GNUNET_MessageHeader *hdr;
75
76 };
77
78
79 /**
80  * Create a message stream tokenizer.
81  *
82  * @param cb function to call on completed messages
83  * @param cb_cls closure for @a cb
84  * @return handle to tokenizer
85  */
86 struct GNUNET_MessageStreamTokenizer *
87 GNUNET_MST_create (GNUNET_MessageTokenizerCallback cb,
88                    void *cb_cls)
89 {
90   struct GNUNET_MessageStreamTokenizer *ret;
91
92   ret = GNUNET_new (struct GNUNET_MessageStreamTokenizer);
93   ret->hdr = GNUNET_malloc (GNUNET_SERVER_MIN_BUFFER_SIZE);
94   ret->curr_buf = GNUNET_SERVER_MIN_BUFFER_SIZE;
95   ret->cb = cb;
96   ret->cb_cls = cb_cls;
97   return ret;
98 }
99
100
101 /**
102  * Add incoming data to the receive buffer and call the
103  * callback for all complete messages.
104  *
105  * @param mst tokenizer to use
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_MST_from_buffer (struct GNUNET_MessageStreamTokenizer *mst,
117                         const char *buf,
118                         size_t size,
119                         int purge,
120                         int one_shot)
121 {
122   const struct GNUNET_MessageHeader *hdr;
123   size_t delta;
124   uint16_t want;
125   char *ibuf;
126   int need_align;
127   unsigned long offset;
128   int ret;
129
130   GNUNET_assert (mst->off <= mst->pos);
131   GNUNET_assert (mst->pos <= mst->curr_buf);
132   LOG (GNUNET_ERROR_TYPE_DEBUG,
133        "Server-mst receives %u bytes with %u bytes already in private buffer\n",
134        (unsigned int) size,
135        (unsigned int) (mst->pos - mst->off));
136   ret = GNUNET_OK;
137   ibuf = (char *) mst->hdr;
138   while (mst->pos > 0)
139   {
140 do_align:
141     GNUNET_assert (mst->pos >= mst->off);
142     if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
143         (0 != (mst->off % ALIGN_FACTOR)))
144     {
145       /* need to align or need more space */
146       mst->pos -= mst->off;
147       memmove (ibuf, &ibuf[mst->off], mst->pos);
148       mst->off = 0;
149     }
150     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
151     {
152       delta =
153           GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
154                       (mst->pos - mst->off), size);
155       GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
156       mst->pos += delta;
157       buf += delta;
158       size -= delta;
159     }
160     if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
161     {
162       if (purge)
163       {
164         mst->off = 0;
165         mst->pos = 0;
166       }
167       return GNUNET_OK;
168     }
169     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
170     want = ntohs (hdr->size);
171     if (want < sizeof (struct GNUNET_MessageHeader))
172     {
173       GNUNET_break_op (0);
174       return GNUNET_SYSERR;
175     }
176     if ( (mst->curr_buf - mst->off < want) &&
177          (mst->off > 0) )
178     {
179       /* can get more space by moving */
180       mst->pos -= mst->off;
181       memmove (ibuf, &ibuf[mst->off], mst->pos);
182       mst->off = 0;
183     }
184     if (mst->curr_buf < want)
185     {
186       /* need to get more space by growing buffer */
187       GNUNET_assert (0 == mst->off);
188       mst->hdr = GNUNET_realloc (mst->hdr, want);
189       ibuf = (char *) mst->hdr;
190       mst->curr_buf = want;
191     }
192     hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
193     if (mst->pos - mst->off < want)
194     {
195       delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
196       GNUNET_assert (mst->pos + delta <= mst->curr_buf);
197       GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
198       mst->pos += delta;
199       buf += delta;
200       size -= delta;
201     }
202     if (mst->pos - mst->off < want)
203     {
204       if (purge)
205       {
206         mst->off = 0;
207         mst->pos = 0;
208       }
209       return GNUNET_OK;
210     }
211     if (one_shot == GNUNET_SYSERR)
212     {
213       /* cannot call callback again, but return value saying that
214        * we have another full message in the buffer */
215       ret = GNUNET_NO;
216       goto copy;
217     }
218     if (one_shot == GNUNET_YES)
219       one_shot = GNUNET_SYSERR;
220     mst->off += want;
221     if (GNUNET_SYSERR == mst->cb (mst->cb_cls,
222                                   hdr))
223       return GNUNET_SYSERR;
224     if (mst->off == mst->pos)
225     {
226       /* reset to beginning of buffer, it's free right now! */
227       mst->off = 0;
228       mst->pos = 0;
229     }
230   }
231   GNUNET_assert (0 == mst->pos);
232   while (size > 0)
233   {
234     LOG (GNUNET_ERROR_TYPE_DEBUG,
235          "Server-mst has %u bytes left in inbound buffer\n",
236          (unsigned int) size);
237     if (size < sizeof (struct GNUNET_MessageHeader))
238       break;
239     offset = (unsigned long) buf;
240     need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
241     if (GNUNET_NO == need_align)
242     {
243       /* can try to do zero-copy and process directly from original buffer */
244       hdr = (const struct GNUNET_MessageHeader *) buf;
245       want = ntohs (hdr->size);
246       if (want < sizeof (struct GNUNET_MessageHeader))
247       {
248         GNUNET_break_op (0);
249         mst->off = 0;
250         return GNUNET_SYSERR;
251       }
252       if (size < want)
253         break;                  /* or not: buffer incomplete, so copy to private buffer... */
254       if (one_shot == GNUNET_SYSERR)
255       {
256         /* cannot call callback again, but return value saying that
257          * we have another full message in the buffer */
258         ret = GNUNET_NO;
259         goto copy;
260       }
261       if (one_shot == GNUNET_YES)
262         one_shot = GNUNET_SYSERR;
263       if (GNUNET_SYSERR == mst->cb (mst->cb_cls,
264                                     hdr))
265         return GNUNET_SYSERR;
266       buf += want;
267       size -= want;
268     }
269     else
270     {
271       /* need to copy to private buffer to align;
272        * yes, we go a bit more spagetti than usual here */
273       goto do_align;
274     }
275   }
276 copy:
277   if ((size > 0) && (!purge))
278   {
279     if (size + mst->pos > mst->curr_buf)
280     {
281       mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
282       ibuf = (char *) mst->hdr;
283       mst->curr_buf = size + mst->pos;
284     }
285     GNUNET_assert (size + mst->pos <= mst->curr_buf);
286     GNUNET_memcpy (&ibuf[mst->pos], buf, size);
287     mst->pos += size;
288   }
289   if (purge)
290   {
291     mst->off = 0;
292     mst->pos = 0;
293   }
294   LOG (GNUNET_ERROR_TYPE_DEBUG,
295        "Server-mst leaves %u bytes in private buffer\n",
296        (unsigned int) (mst->pos - mst->off));
297   return ret;
298 }
299
300
301 /**
302  * Add incoming data to the receive buffer and call the
303  * callback for all complete messages.
304  *
305  * @param mst tokenizer to use
306  * @param buf input data to add
307  * @param size number of bytes in @a buf
308  * @param purge should any excess bytes in the buffer be discarded
309  *       (i.e. for packet-based services like UDP)
310  * @param one_shot only call callback once, keep rest of message in buffer
311  * @return #GNUNET_OK if we are done processing (need more data)
312  *         #GNUNET_NO if one_shot was set and we have another message ready
313  *         #GNUNET_SYSERR if the data stream is corrupt
314  */
315 int
316 GNUNET_MST_read (struct GNUNET_MessageStreamTokenizer *mst,
317                  struct GNUNET_NETWORK_Handle *sock,
318                  int purge,
319                  int one_shot)
320 {
321   GNUNET_assert (0); // not implemented
322   return GNUNET_SYSERR;
323 }
324
325
326 /**
327  * Obtain the next message from the @a mst, assuming that
328  * there are more unprocessed messages in the internal buffer
329  * of the @a mst.
330  *
331  * @param mst tokenizer to use
332  * @param one_shot only call callback once, keep rest of message in buffer
333  * @return #GNUNET_OK if we are done processing (need more data)
334  *         #GNUNET_NO if one_shot was set and we have another message ready
335  *         #GNUNET_SYSERR if the data stream is corrupt
336  */
337 int
338 GNUNET_MST_next (struct GNUNET_MessageStreamTokenizer *mst,
339                  int one_shot)
340 {
341   return GNUNET_MST_from_buffer (mst,
342                                  NULL,
343                                  0,
344                                  GNUNET_NO,
345                                  one_shot);
346 }
347
348
349 /**
350  * Destroys a tokenizer.
351  *
352  * @param mst tokenizer to destroy
353  */
354 void
355 GNUNET_MST_destroy (struct GNUNET_MessageStreamTokenizer *mst)
356 {
357   GNUNET_free (mst->hdr);
358   GNUNET_free (mst);
359 }
360
361
362
363 /* end of server_mst.c */