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