net_mem.c
Go to the documentation of this file.
1 /**
2  * @file net_mem.c
3  * @brief Memory management
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL MEM_TRACE_LEVEL
31 
32 //Dependencies
33 #include "core/net.h"
34 #include "core/net_mem.h"
35 #include "debug.h"
36 
37 //Maximum number of chunks for dynamically allocated buffers
38 #if (IPV4_SUPPORT == ENABLED && IPV6_SUPPORT == ENABLED)
39  #define MAX_CHUNK_COUNT (N(MAX(IPV4_MAX_FRAG_DATAGRAM_SIZE, IPV6_MAX_FRAG_DATAGRAM_SIZE)) + 3)
40 #elif (IPV4_SUPPORT == ENABLED)
41  #define MAX_CHUNK_COUNT (N(IPV4_MAX_FRAG_DATAGRAM_SIZE) + 3)
42 #elif (IPV6_SUPPORT == ENABLED)
43  #define MAX_CHUNK_COUNT (N(IPV6_MAX_FRAG_DATAGRAM_SIZE) + 3)
44 #endif
45 
46 //Use fixed-size blocks allocation?
47 #if (NET_MEM_POOL_SUPPORT == ENABLED)
48 
49 //Mutex preventing simultaneous access to the memory pool
50 static OsMutex memPoolMutex;
51 //Memory pool
53 //Allocation table
54 static bool_t memPoolAllocTable[NET_MEM_POOL_BUFFER_COUNT];
55 //Number of buffers currently allocated
57 //Maximum number of buffers that have been allocated so far
59 
60 #endif
61 
62 
63 /**
64  * @brief Memory pool initialization
65  * @return Error code
66  **/
67 
69 {
70 //Use fixed-size blocks allocation?
71 #if (NET_MEM_POOL_SUPPORT == ENABLED)
72  //Create a mutex to prevent simultaneous access to the memory pool
73  if(!osCreateMutex(&memPoolMutex))
74  {
75  //Failed to create mutex
77  }
78 
79  //Clear allocation table
80  memset(memPoolAllocTable, 0, sizeof(memPoolAllocTable));
81 
82  //Clear statistics
84  memPoolMaxUsage = 0;
85 #endif
86 
87  //Successful initialization
88  return NO_ERROR;
89 }
90 
91 
92 /**
93  * @brief Allocate a memory block
94  * @param[in] size Bytes to allocate
95  * @return Pointer to the allocated space or NULL if there is insufficient memory available
96  **/
97 
98 void *memPoolAlloc(size_t size)
99 {
100 #if (NET_MEM_POOL_SUPPORT == ENABLED)
101  uint_t i;
102 #endif
103 
104  //Pointer to the allocated memory block
105  void *p = NULL;
106 
107  //Debug message
108  TRACE_DEBUG("Allocating %" PRIuSIZE " bytes...\r\n", size);
109 
110 //Use fixed-size blocks allocation?
111 #if (NET_MEM_POOL_SUPPORT == ENABLED)
112  //Acquire exclusive access to the memory pool
113  osAcquireMutex(&memPoolMutex);
114 
115  //Enforce block size
116  if(size <= NET_MEM_POOL_BUFFER_SIZE)
117  {
118  //Loop through allocation table
119  for(i = 0; i < NET_MEM_POOL_BUFFER_COUNT; i++)
120  {
121  //Check whether the current block is free
122  if(!memPoolAllocTable[i])
123  {
124  //Mark the current entry as used
125  memPoolAllocTable[i] = TRUE;
126  //Point to the corresponding memory block
127  p = memPool[i];
128 
129  //Update statistics
131  //Maximum number of buffers that have been allocated so far
133 
134  //Exit immediately
135  break;
136  }
137  }
138  }
139 
140  //Release exclusive access to the memory pool
141  osReleaseMutex(&memPoolMutex);
142 #else
143  //Allocate a memory block
144  p = osAllocMem(size);
145 #endif
146 
147  //Failed to allocate memory?
148  if(!p)
149  {
150  //Debug message
151  TRACE_WARNING("Memory allocation failed!\r\n");
152  }
153 
154  //Return a pointer to the allocated memory block
155  return p;
156 }
157 
158 
159 /**
160  * @brief Release a memory block
161  * @param[in] p Previously allocated memory block to be freed
162  **/
163 
164 void memPoolFree(void *p)
165 {
166 //Use fixed-size blocks allocation?
167 #if (NET_MEM_POOL_SUPPORT == ENABLED)
168  uint_t i;
169 
170  //Acquire exclusive access to the memory pool
171  osAcquireMutex(&memPoolMutex);
172 
173  //Loop through allocation table
174  for(i = 0; i < NET_MEM_POOL_BUFFER_COUNT; i++)
175  {
176  if(memPool[i] == p)
177  {
178  //Mark the current block as free
179  memPoolAllocTable[i] = FALSE;
180 
181  //Update statistics
183 
184  //Exit immediately
185  break;
186  }
187  }
188 
189  //Release exclusive access to the memory pool
190  osReleaseMutex(&memPoolMutex);
191 #else
192  //Release memory block
193  osFreeMem(p);
194 #endif
195 }
196 
197 
198 /**
199  * @brief Get memory pool usage
200  * @param[out] currentUsage Number of buffers currently allocated
201  * @param[out] maxUsage Maximum number of buffers that have been allocated so far
202  * @param[out] size Total number of buffers in the memory pool
203  **/
204 
205 void memPoolGetStats(uint_t *currentUsage, uint_t *maxUsage, uint_t *size)
206 {
207 //Use fixed-size blocks allocation?
208 #if (NET_MEM_POOL_SUPPORT == ENABLED)
209  //Number of buffers currently allocated
210  if(currentUsage != NULL)
211  *currentUsage = memPoolCurrentUsage;
212 
213  //Maximum number of buffers that have been allocated so far
214  if(maxUsage != NULL)
215  *maxUsage = memPoolMaxUsage;
216 
217  //Total number of buffers in the memory pool
218  if(size != NULL)
220 #else
221  //Memory pool is not used...
222  if(currentUsage != NULL)
223  *currentUsage = 0;
224 
225  if(maxUsage != NULL)
226  *maxUsage = 0;
227 
228  if(size != NULL)
229  *size = 0;
230 #endif
231 }
232 
233 
234 /**
235  * @brief Allocate a multi-part buffer
236  * @param[in] length Desired length
237  * @return Pointer to the allocated buffer or NULL if there is
238  * insufficient memory available
239  **/
240 
242 {
243  error_t error;
244  NetBuffer *buffer;
245 
246  //Allocate memory to hold the multi-part buffer
248  //Failed to allocate memory?
249  if(buffer == NULL)
250  return NULL;
251 
252  //The multi-part buffer consists of a single chunk
253  buffer->chunkCount = 1;
254  buffer->maxChunkCount = MAX_CHUNK_COUNT;
255  buffer->chunk[0].address = (uint8_t *) buffer + CHUNKED_BUFFER_HEADER_SIZE;
257  buffer->chunk[0].size = 0;
258 
259  //Adjust the length of the buffer
260  error = netBufferSetLength(buffer, length);
261  //Any error to report?
262  if(error)
263  {
264  //Clean up side effects
265  netBufferFree(buffer);
266  //Report an failure
267  return NULL;
268  }
269 
270  //Successful memory allocation
271  return buffer;
272 }
273 
274 
275 /**
276  * @brief Dispose a multi-part buffer
277  * @param[in] buffer Pointer to the multi-part buffer to be released
278  **/
279 
281 {
282  //Properly dispose data chunks
283  netBufferSetLength(buffer, 0);
284  //Release multi-part buffer
285  memPoolFree(buffer);
286 }
287 
288 
289 /**
290  * @brief Get the actual length of a multi-part buffer
291  * @param[in] buffer Pointer to a multi-part buffer
292  * @return Actual length in bytes
293  **/
294 
295 size_t netBufferGetLength(const NetBuffer *buffer)
296 {
297  uint_t i;
298 
299  //Total length
300  size_t length = 0;
301 
302  //Loop through data chunks
303  for(i = 0; i < buffer->chunkCount; i++)
304  length += buffer->chunk[i].length;
305 
306  //Return total length
307  return length;
308 }
309 
310 
311 /**
312  * @brief Adjust the length of a multi-part buffer
313  * @param[in] buffer Pointer to the multi-part buffer whose length is to be changed
314  * @param[in] length Desired length
315  * @return Error code
316  **/
317 
319 {
320  uint_t i;
321  uint_t chunkCount;
322  ChunkDesc *chunk;
323 
324  //Get the actual number of chunks
325  chunkCount = buffer->chunkCount;
326 
327  //Loop through data chunks
328  for(i = 0; i < chunkCount && length > 0; i++)
329  {
330  //Point to the chunk descriptor;
331  chunk = &buffer->chunk[i];
332 
333  //Adjust the length of the current chunk when possible
334  if(length <= chunk->length)
335  {
336  chunk->length = length;
337  }
338  else if(chunk->size > 0 && i == (chunkCount - 1))
339  {
340  chunk->length = MIN(length, chunk->size);
341  }
342 
343  //Prepare to process next chunk
344  length -= chunk->length;
345  }
346 
347  //The size of the buffer should be decreased?
348  if(!length)
349  {
350  //Adjust the number of chunks
351  buffer->chunkCount = i;
352 
353  //Delete unnecessary data chunks
354  while(i < chunkCount)
355  {
356  //Point to the chunk descriptor;
357  chunk = &buffer->chunk[i];
358 
359  //Release previously allocated memory
360  if(chunk->size > 0)
361  memPoolFree(chunk->address);
362 
363  //Mark the current chunk as free
364  chunk->address = NULL;
365  chunk->length = 0;
366  chunk->size = 0;
367 
368  //Next chunk
369  i++;
370  }
371  }
372  //The size of the buffer should be increased?
373  else
374  {
375  //Add as many chunks as necessary
376  while(i < buffer->maxChunkCount && length > 0)
377  {
378  //Point to the chunk descriptor;
379  chunk = &buffer->chunk[i];
380 
381  //Allocate memory to hold a new chunk
383  //Failed to allocate memory?
384  if(!chunk->address)
385  return ERROR_OUT_OF_MEMORY;
386 
387  //Allocated memory
389  //Actual length of the data chunk
391 
392  //Prepare to process next chunk
393  length -= chunk->length;
394  buffer->chunkCount++;
395  i++;
396  }
397  }
398 
399  //Return status code
400  return (length > 0) ? ERROR_OUT_OF_RESOURCES : NO_ERROR;
401 }
402 
403 
404 /**
405  * @brief Returns a pointer to the data at the specified position
406  * @param[in] buffer Pointer to a multi-part buffer
407  * @param[in] offset Offset from the beginning of the buffer
408  * @return Pointer the data at the specified position
409  **/
410 
411 void *netBufferAt(const NetBuffer *buffer, size_t offset)
412 {
413  uint_t i;
414 
415  //Loop through data chunks
416  for(i = 0; i < buffer->chunkCount; i++)
417  {
418  //The data at the specified offset resides in the current chunk?
419  if(offset < buffer->chunk[i].length)
420  return (uint8_t *) buffer->chunk[i].address + offset;
421 
422  //Jump to the next chunk
423  offset -= buffer->chunk[i].length;
424  }
425 
426  //Invalid offset...
427  return NULL;
428 }
429 
430 
431 /**
432  * @brief Concatenate two multi-part buffers
433  * @param[out] dest Pointer to the destination buffer
434  * @param[in] src Pointer to the source buffer
435  * @param[in] srcOffset Read offset
436  * @param[in] length Number of bytes to read from the source buffer
437  * @return Error code
438  **/
439 
441  const NetBuffer *src, size_t srcOffset, size_t length)
442 {
443  uint_t i;
444  uint_t j;
445 
446  //Skip the beginning of the source data
447  for(j = 0; j < src->chunkCount; j++)
448  {
449  //The data at the specified offset resides in the current chunk?
450  if(srcOffset < src->chunk[j].length)
451  break;
452 
453  //Jump to the next chunk
454  srcOffset -= src->chunk[j].length;
455  }
456 
457  //Invalid offset?
458  if(j >= src->chunkCount)
460 
461  //Position to the end of the destination data
462  i = dest->chunkCount;
463 
464  //Copy data blocks
465  while(length > 0 && i < dest->maxChunkCount && j < src->chunkCount)
466  {
467  //Copy current block
468  dest->chunk[i].address = (uint8_t *) src->chunk[j].address + srcOffset;
469  dest->chunk[i].length = src->chunk[j].length - srcOffset;
470  dest->chunk[i].size = 0;
471 
472  //Limit the number of bytes to copy
473  if(length < dest->chunk[i].length)
474  dest->chunk[i].length = length;
475 
476  //Decrement the number of remaining bytes
477  length -= dest->chunk[i].length;
478  //Increment the number of chunks
479  dest->chunkCount++;
480 
481  //Adjust variables
482  srcOffset = 0;
483  i++;
484  j++;
485  }
486 
487  //Return status code
488  return (length > 0) ? ERROR_FAILURE : NO_ERROR;
489 }
490 
491 
492 /**
493  * @brief Copy data between multi-part buffers
494  * @param[out] dest Pointer to the destination buffer
495  * @param[in] destOffset Write offset
496  * @param[in] src Pointer to the source buffer
497  * @param[in] srcOffset Read offset
498  * @param[in] length Number of bytes to be copied
499  * @return Error code
500  **/
501 
502 error_t netBufferCopy(NetBuffer *dest, size_t destOffset,
503  const NetBuffer *src, size_t srcOffset, size_t length)
504 {
505  uint_t i;
506  uint_t j;
507  uint_t n;
508  uint8_t *p;
509  uint8_t *q;
510 
511  //Skip the beginning of the source data
512  for(i = 0; i < dest->chunkCount; i++)
513  {
514  //The data at the specified offset resides in the current chunk?
515  if(destOffset < dest->chunk[i].length)
516  break;
517 
518  //Jump to the next chunk
519  destOffset -= dest->chunk[i].length;
520  }
521 
522  //Invalid offset?
523  if(i >= dest->chunkCount)
525 
526  //Skip the beginning of the source data
527  for(j = 0; j < src->chunkCount; j++)
528  {
529  //The data at the specified offset resides in the current chunk?
530  if(srcOffset < src->chunk[j].length)
531  break;
532 
533  //Jump to the next chunk
534  srcOffset -= src->chunk[j].length;
535  }
536 
537  //Invalid offset?
538  if(j >= src->chunkCount)
540 
541  while(length > 0 && i < dest->chunkCount && j < src->chunkCount)
542  {
543  //Point to the first data byte
544  p = (uint8_t *) dest->chunk[i].address + destOffset;
545  q = (uint8_t *) src->chunk[j].address + srcOffset;
546 
547  //Compute the number of bytes to copy
548  n = MIN(length, dest->chunk[i].length - destOffset);
549  n = MIN(n, src->chunk[j].length - srcOffset);
550 
551  //Copy data
552  memcpy(p, q, n);
553 
554  destOffset += n;
555  srcOffset += n;
556  length -= n;
557 
558  if(destOffset >= dest->chunk[i].length)
559  {
560  destOffset = 0;
561  i++;
562  }
563 
564  if(srcOffset >= src->chunk[j].length)
565  {
566  srcOffset = 0;
567  j++;
568  }
569  }
570 
571  //Return status code
572  return (length > 0) ? ERROR_FAILURE : NO_ERROR;
573 }
574 
575 
576 /**
577  * @brief Append data a multi-part buffer
578  * @param[out] dest Pointer to a multi-part buffer
579  * @param[in] src User buffer containing the data to be appended
580  * @param[in] length Number of bytes in the user buffer
581  * @return Error code
582  **/
583 
584 error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
585 {
586  uint_t i;
587 
588  //Make sure there is enough space to add an extra chunk
589  if(dest->chunkCount >= dest->maxChunkCount)
590  return ERROR_FAILURE;
591 
592  //Position to the end of the buffer
593  i = dest->chunkCount;
594 
595  //Insert a new chunk at the end of the list
596  dest->chunk[i].address = (void *) src;
597  dest->chunk[i].length = length;
598  dest->chunk[i].size = 0;
599 
600  //Increment the number of chunks
601  dest->chunkCount++;
602 
603  //Successful processing
604  return NO_ERROR;
605 }
606 
607 
608 /**
609  * @brief Write data to a multi-part buffer
610  * @param[out] dest Pointer to a multi-part buffer
611  * @param[in] destOffset Offset from the beginning of the multi-part buffer
612  * @param[in] src User buffer containing the data to be written
613  * @param[in] length Number of bytes to copy
614  * @return Actual number of bytes copied
615  **/
616 
618  size_t destOffset, const void *src, size_t length)
619 {
620  uint_t i;
621  uint_t n;
622  size_t totalLength;
623  uint8_t *p;
624 
625  //Total number of bytes written
626  totalLength = 0;
627 
628  //Loop through data chunks
629  for(i = 0; i < dest->chunkCount && totalLength < length; i++)
630  {
631  //Is there any data to copy in the current chunk?
632  if(destOffset < dest->chunk[i].length)
633  {
634  //Point to the first byte to be written
635  p = (uint8_t *) dest->chunk[i].address + destOffset;
636  //Compute the number of bytes to copy at a time
637  n = MIN(length - totalLength, dest->chunk[i].length - destOffset);
638 
639  //Copy data
640  memcpy(p, src, n);
641 
642  //Advance read pointer
643  src = (uint8_t *) src + n;
644  //Total number of bytes written
645  totalLength += n;
646  //Process the next block from the start
647  destOffset = 0;
648  }
649  else
650  {
651  //Skip the current chunk
652  destOffset -= dest->chunk[i].length;
653  }
654  }
655 
656  //Return the actual number of bytes written
657  return totalLength;
658 }
659 
660 
661 /**
662  * @brief Read data from a multi-part buffer
663  * @param[out] dest Pointer to the buffer where to return the data
664  * @param[in] src Pointer to a multi-part buffer
665  * @param[in] srcOffset Offset from the beginning of the multi-part buffer
666  * @param[in] length Number of bytes to copy
667  * @return Actual number of bytes copied
668  **/
669 
670 size_t netBufferRead(void *dest, const NetBuffer *src,
671  size_t srcOffset, size_t length)
672 {
673  uint_t i;
674  uint_t n;
675  size_t totalLength;
676  uint8_t *p;
677 
678  //Total number of bytes copied
679  totalLength = 0;
680 
681  //Loop through data chunks
682  for(i = 0; i < src->chunkCount && totalLength < length; i++)
683  {
684  //Is there any data to copy from the current chunk?
685  if(srcOffset < src->chunk[i].length)
686  {
687  //Point to the first byte to be read
688  p = (uint8_t *) src->chunk[i].address + srcOffset;
689  //Compute the number of bytes to copy at a time
690  n = MIN(length - totalLength, src->chunk[i].length - srcOffset);
691 
692  //Copy data
693  memcpy(dest, p, n);
694 
695  //Advance write pointer
696  dest = (uint8_t *) dest + n;
697  //Total number of bytes copied
698  totalLength += n;
699  //Process the next block from the start
700  srcOffset = 0;
701  }
702  else
703  {
704  //Skip the current chunk
705  srcOffset -= src->chunk[i].length;
706  }
707  }
708 
709  //Return the actual number of bytes copied
710  return totalLength;
711 }
#define NET_MEM_POOL_BUFFER_SIZE
Definition: net_mem.h:53
bool_t osCreateMutex(OsMutex *mutex)
Create a mutex object.
void osFreeMem(void *p)
Release a previously allocated memory block.
TCP/IP stack core.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:280
Debugging facilities.
error_t netBufferCopy(NetBuffer *dest, size_t destOffset, const NetBuffer *src, size_t srcOffset, size_t length)
Copy data between multi-part buffers.
Definition: net_mem.c:502
uint8_t p
Definition: ndp.h:295
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:295
Generic error code.
Definition: error.h:43
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:318
Invalid parameter.
Definition: error.h:45
void * memPoolAlloc(size_t size)
Allocate a memory block.
Definition: net_mem.c:98
uint_t memPoolMaxUsage
Definition: net_mem.c:58
void * address
Definition: net_mem.h:76
#define MAX(a, b)
Definition: os_port.h:64
#define TRUE
Definition: os_port.h:48
uint_t memPoolCurrentUsage
Definition: net_mem.c:56
error_t netBufferConcat(NetBuffer *dest, const NetBuffer *src, size_t srcOffset, size_t length)
Concatenate two multi-part buffers.
Definition: net_mem.c:440
ChunkDesc chunk[]
Definition: net_mem.h:90
uint16_t size
Definition: net_mem.h:78
uint_t maxChunkCount
Definition: net_mem.h:89
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:411
void memPoolFree(void *p)
Release a memory block.
Definition: net_mem.c:164
size_t netBufferRead(void *dest, const NetBuffer *src, size_t srcOffset, size_t length)
Read data from a multi-part buffer.
Definition: net_mem.c:670
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:86
#define MIN(a, b)
Definition: os_port.h:60
NetBuffer * netBufferAlloc(size_t length)
Allocate a multi-part buffer.
Definition: net_mem.c:241
#define NET_MEM_POOL_BUFFER_COUNT
Definition: net_mem.h:46
Success.
Definition: error.h:42
uint16_t length
Definition: net_mem.h:77
void * osAllocMem(size_t size)
Allocate a memory block.
error_t
Error codes.
Definition: error.h:40
#define TRACE_WARNING(...)
Definition: debug.h:78
unsigned int uint_t
Definition: compiler_port.h:43
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:584
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
#define PRIuSIZE
Definition: compiler_port.h:72
Memory management.
Mutex object.
#define CHUNKED_BUFFER_HEADER_SIZE
Definition: net_mem.h:59
size_t netBufferWrite(NetBuffer *dest, size_t destOffset, const void *src, size_t length)
Write data to a multi-part buffer.
Definition: net_mem.c:617
error_t memPoolInit(void)
Memory pool initialization.
Definition: net_mem.c:68
#define MAX_CHUNK_COUNT
Definition: net_mem.c:39
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
#define FALSE
Definition: os_port.h:44
int bool_t
Definition: compiler_port.h:47
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
Structure describing a chunk of data.
Definition: net_mem.h:74
void memPoolGetStats(uint_t *currentUsage, uint_t *maxUsage, uint_t *size)
Get memory pool usage.
Definition: net_mem.c:205
uint_t chunkCount
Definition: net_mem.h:88
#define TRACE_DEBUG(...)
Definition: debug.h:98