2 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
4 * Author: Jonathan Bastien-Filiatrault
6 * This file is part of GNUTLS.
8 * The GNUTLS library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 3 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include "gnutls_mbuffers.h"
24 #include "gnutls_errors.h"
26 /* Here be mbuffers */
28 /* A note on terminology:
30 * Variables named bufel designate a single buffer segment (mbuffer_st
31 * type). This type is textually referred to as a "segment" or a
34 * Variables named buf desigate a chain of buffer segments
35 * (mbuffer_head_st type). This type is textually referred to as a
36 * "buffer head" or simply as "buffer".
40 * - Make existing code easier to understand.
41 * - Make common operations more efficient by avoiding unnecessary
43 * - Provide a common datatype with a well-known interface to move
44 * data around and through the multiple protocol layers.
45 * - Enable a future implementation of DTLS, which needs the concept
46 * of record boundaries.
50 /* Initialize a buffer head.
55 _mbuffer_head_init (mbuffer_head_st
* buf
)
64 /* Deallocate all buffer segments and reset the buffer head.
67 * n: Number of segments currently in the buffer.
70 _mbuffer_head_clear (mbuffer_head_st
* buf
)
72 mbuffer_st
*bufel
, *next
;
74 for (bufel
= buf
->head
; bufel
!= NULL
; bufel
= next
)
80 _mbuffer_head_init (buf
);
83 /* Append a segment to the end of this buffer.
88 _mbuffer_enqueue (mbuffer_head_st
* buf
, mbuffer_st
* bufel
)
91 bufel
->prev
= buf
->tail
;
94 buf
->byte_length
+= bufel
->msg
.size
- bufel
->mark
;
96 if (buf
->tail
!= NULL
)
97 buf
->tail
->next
= bufel
;
103 /* Remove a segment from the buffer.
107 * Returns the buffer following it.
110 _mbuffer_dequeue (mbuffer_head_st
* buf
, mbuffer_st
* bufel
)
112 mbuffer_st
* ret
= bufel
->next
;
114 if (buf
->tail
== bufel
) /* if last */
115 buf
->tail
= bufel
->prev
;
117 if (buf
->head
== bufel
) /* if first */
118 buf
->head
= bufel
->next
;
121 bufel
->prev
->next
= bufel
->next
;
124 bufel
->next
->prev
= NULL
;
127 buf
->byte_length
-= bufel
->msg
.size
- bufel
->mark
;
129 bufel
->next
= bufel
->prev
= NULL
;
134 /* Get a reference to the first segment of the buffer and
135 * remove it from the list.
137 * Used to start iteration.
142 _mbuffer_head_pop_first (mbuffer_head_st
* buf
)
144 mbuffer_st
*bufel
= buf
->head
;
146 if (buf
->head
== NULL
)
149 _mbuffer_dequeue(buf
, bufel
);
154 /* Get a reference to the first segment of the buffer and its data.
156 * Used to start iteration or to peek at the data.
161 _mbuffer_head_get_first (mbuffer_head_st
* buf
, gnutls_datum_t
* msg
)
163 mbuffer_st
*bufel
= buf
->head
;
169 msg
->data
= bufel
->msg
.data
+ bufel
->mark
;
170 msg
->size
= bufel
->msg
.size
- bufel
->mark
;
181 /* Get a reference to the next segment of the buffer and its data.
183 * Used to iterate over the buffer segments.
188 _mbuffer_head_get_next (mbuffer_st
* cur
, gnutls_datum_t
* msg
)
190 mbuffer_st
*bufel
= cur
->next
;
196 msg
->data
= bufel
->msg
.data
+ bufel
->mark
;
197 msg
->size
= bufel
->msg
.size
- bufel
->mark
;
208 /* Remove the first segment from the buffer.
210 * Used to dequeue data from the buffer. Not yet exposed in the
211 * internal interface since it is not yet needed outside of this unit.
216 remove_front (mbuffer_head_st
* buf
)
218 mbuffer_st
*bufel
= buf
->head
;
223 _mbuffer_dequeue(buf
, bufel
);
227 /* Remove a specified number of bytes from the start of the buffer.
229 * Useful for uses that treat the buffer as a simple array of bytes.
231 * If more than one mbuffer_st have been removed it
232 * returns 1, 0 otherwise and an error code on error.
235 * n: Number of segments needed to remove the specified amount of data.
238 _mbuffer_head_remove_bytes (mbuffer_head_st
* buf
, size_t bytes
)
241 mbuffer_st
*bufel
, *next
;
244 if (bytes
> buf
->byte_length
)
247 return GNUTLS_E_INVALID_REQUEST
;
250 for (bufel
= buf
->head
; bufel
!= NULL
&& left
> 0; bufel
= next
)
254 if (left
>= (bufel
->msg
.size
- bufel
->mark
))
256 left
-= (bufel
->msg
.size
- bufel
->mark
);
263 buf
->byte_length
-= left
;
270 /* Allocate a buffer segment. The segment is not initially "owned" by
273 * maximum_size: Amount of data that this segment can contain.
274 * size: Amount of useful data that is contained in this
275 * buffer. Generally 0, but this is a shortcut when a fixed amount of
276 * data will immediately be added to this segment.
278 * Returns the segment or NULL on error.
283 _mbuffer_alloc (size_t payload_size
, size_t maximum_size
)
287 st
= gnutls_calloc (1, maximum_size
+ sizeof (mbuffer_st
));
294 /* payload points after the mbuffer_st structure */
295 st
->msg
.data
= (uint8_t *) st
+ sizeof (mbuffer_st
);
296 st
->msg
.size
= payload_size
;
297 st
->maximum_size
= maximum_size
;
302 /* Copy data into a segment. The segment must not be part of a buffer
303 * head when using this function.
305 * Bounds checking is performed by this function.
307 * Returns 0 on success or an error code otherwise.
310 * n: number of bytes to copy
313 _mbuffer_append_data (mbuffer_st
* bufel
, void *newdata
, size_t newdata_size
)
315 if (bufel
->msg
.size
+ newdata_size
<= bufel
->maximum_size
)
317 memcpy (&bufel
->msg
.data
[bufel
->msg
.size
], newdata
, newdata_size
);
318 bufel
->msg
.size
+= newdata_size
;
323 return GNUTLS_E_INVALID_REQUEST
;
329 /* Takes a buffer in multiple chunks and puts all the data in a single
330 * contiguous segment.
332 * Returns 0 on success or an error code otherwise.
335 * n: number of segments initially in the buffer
338 _mbuffer_linearize (mbuffer_head_st
* buf
)
340 mbuffer_st
*bufel
, *cur
;
344 if (buf
->length
<= 1)
348 bufel
= _mbuffer_alloc (buf
->byte_length
, buf
->byte_length
);
352 return GNUTLS_E_MEMORY_ERROR
;
355 for (cur
= _mbuffer_head_get_first (buf
, &msg
);
356 msg
.data
!= NULL
; cur
= _mbuffer_head_get_next (cur
, &msg
))
358 memcpy (&bufel
->msg
.data
[pos
], msg
.data
, msg
.size
);
362 _mbuffer_head_clear (buf
);
363 _mbuffer_enqueue (buf
, bufel
);