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.0
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  memPoolFree(chunk->address);
366 
367  //Mark the current chunk as free
368  chunk->address = NULL;
369  chunk->length = 0;
370  chunk->size = 0;
371 
372  //Next chunk
373  i++;
374  }
375  }
376  //The size of the buffer should be increased?
377  else
378  {
379  //Add as many chunks as necessary
380  while(i < buffer->maxChunkCount && length > 0)
381  {
382  //Point to the chunk descriptor;
383  chunk = &buffer->chunk[i];
384 
385  //Allocate memory to hold a new chunk
387  //Failed to allocate memory?
388  if(!chunk->address)
389  return ERROR_OUT_OF_MEMORY;
390 
391  //Allocated memory
393  //Actual length of the data chunk
395 
396  //Prepare to process next chunk
397  length -= chunk->length;
398  buffer->chunkCount++;
399  i++;
400  }
401  }
402 
403  //Return status code
404  return (length > 0) ? ERROR_OUT_OF_RESOURCES : NO_ERROR;
405 }
406 
407 
408 /**
409  * @brief Returns a pointer to the data at the specified position
410  * @param[in] buffer Pointer to a multi-part buffer
411  * @param[in] offset Offset from the beginning of the buffer
412  * @return Pointer the data at the specified position
413  **/
414 
415 void *netBufferAt(const NetBuffer *buffer, size_t offset)
416 {
417  uint_t i;
418 
419  //Loop through data chunks
420  for(i = 0; i < buffer->chunkCount; i++)
421  {
422  //The data at the specified offset resides in the current chunk?
423  if(offset < buffer->chunk[i].length)
424  return (uint8_t *) buffer->chunk[i].address + offset;
425 
426  //Jump to the next chunk
427  offset -= buffer->chunk[i].length;
428  }
429 
430  //Invalid offset...
431  return NULL;
432 }
433 
434 
435 /**
436  * @brief Concatenate two multi-part buffers
437  * @param[out] dest Pointer to the destination buffer
438  * @param[in] src Pointer to the source buffer
439  * @param[in] srcOffset Read offset
440  * @param[in] length Number of bytes to read from the source buffer
441  * @return Error code
442  **/
443 
445  const NetBuffer *src, size_t srcOffset, size_t length)
446 {
447  uint_t i;
448  uint_t j;
449 
450  //Skip the beginning of the source data
451  for(j = 0; j < src->chunkCount; j++)
452  {
453  //The data at the specified offset resides in the current chunk?
454  if(srcOffset < src->chunk[j].length)
455  break;
456 
457  //Jump to the next chunk
458  srcOffset -= src->chunk[j].length;
459  }
460 
461  //Invalid offset?
462  if(j >= src->chunkCount)
464 
465  //Position to the end of the destination data
466  i = dest->chunkCount;
467 
468  //Copy data blocks
469  while(length > 0 && i < dest->maxChunkCount && j < src->chunkCount)
470  {
471  //Copy current block
472  dest->chunk[i].address = (uint8_t *) src->chunk[j].address + srcOffset;
473  dest->chunk[i].length = src->chunk[j].length - srcOffset;
474  dest->chunk[i].size = 0;
475 
476  //Limit the number of bytes to copy
477  if(length < dest->chunk[i].length)
478  dest->chunk[i].length = length;
479 
480  //Decrement the number of remaining bytes
481  length -= dest->chunk[i].length;
482  //Increment the number of chunks
483  dest->chunkCount++;
484 
485  //Adjust variables
486  srcOffset = 0;
487  i++;
488  j++;
489  }
490 
491  //Return status code
492  return (length > 0) ? ERROR_FAILURE : NO_ERROR;
493 }
494 
495 
496 /**
497  * @brief Copy data between multi-part buffers
498  * @param[out] dest Pointer to the destination buffer
499  * @param[in] destOffset Write offset
500  * @param[in] src Pointer to the source buffer
501  * @param[in] srcOffset Read offset
502  * @param[in] length Number of bytes to be copied
503  * @return Error code
504  **/
505 
506 error_t netBufferCopy(NetBuffer *dest, size_t destOffset,
507  const NetBuffer *src, size_t srcOffset, size_t length)
508 {
509  uint_t i;
510  uint_t j;
511  uint_t n;
512  uint8_t *p;
513  uint8_t *q;
514 
515  //Skip the beginning of the source data
516  for(i = 0; i < dest->chunkCount; i++)
517  {
518  //The data at the specified offset resides in the current chunk?
519  if(destOffset < dest->chunk[i].length)
520  break;
521 
522  //Jump to the next chunk
523  destOffset -= dest->chunk[i].length;
524  }
525 
526  //Invalid offset?
527  if(i >= dest->chunkCount)
529 
530  //Skip the beginning of the source data
531  for(j = 0; j < src->chunkCount; j++)
532  {
533  //The data at the specified offset resides in the current chunk?
534  if(srcOffset < src->chunk[j].length)
535  break;
536 
537  //Jump to the next chunk
538  srcOffset -= src->chunk[j].length;
539  }
540 
541  //Invalid offset?
542  if(j >= src->chunkCount)
544 
545  while(length > 0 && i < dest->chunkCount && j < src->chunkCount)
546  {
547  //Point to the first data byte
548  p = (uint8_t *) dest->chunk[i].address + destOffset;
549  q = (uint8_t *) src->chunk[j].address + srcOffset;
550 
551  //Compute the number of bytes to copy
552  n = MIN(length, dest->chunk[i].length - destOffset);
553  n = MIN(n, src->chunk[j].length - srcOffset);
554 
555  //Copy data
556  osMemcpy(p, q, n);
557 
558  destOffset += n;
559  srcOffset += n;
560  length -= n;
561 
562  if(destOffset >= dest->chunk[i].length)
563  {
564  destOffset = 0;
565  i++;
566  }
567 
568  if(srcOffset >= src->chunk[j].length)
569  {
570  srcOffset = 0;
571  j++;
572  }
573  }
574 
575  //Return status code
576  return (length > 0) ? ERROR_FAILURE : NO_ERROR;
577 }
578 
579 
580 /**
581  * @brief Append data a multi-part buffer
582  * @param[out] dest Pointer to a multi-part buffer
583  * @param[in] src User buffer containing the data to be appended
584  * @param[in] length Number of bytes in the user buffer
585  * @return Error code
586  **/
587 
588 error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
589 {
590  uint_t i;
591 
592  //Make sure there is enough space to add an extra chunk
593  if(dest->chunkCount >= dest->maxChunkCount)
594  return ERROR_FAILURE;
595 
596  //Position to the end of the buffer
597  i = dest->chunkCount;
598 
599  //Insert a new chunk at the end of the list
600  dest->chunk[i].address = (void *) src;
601  dest->chunk[i].length = length;
602  dest->chunk[i].size = 0;
603 
604  //Increment the number of chunks
605  dest->chunkCount++;
606 
607  //Successful processing
608  return NO_ERROR;
609 }
610 
611 
612 /**
613  * @brief Write data to a multi-part buffer
614  * @param[out] dest Pointer to a multi-part buffer
615  * @param[in] destOffset Offset from the beginning of the multi-part buffer
616  * @param[in] src User buffer containing the data to be written
617  * @param[in] length Number of bytes to copy
618  * @return Actual number of bytes copied
619  **/
620 
622  size_t destOffset, const void *src, size_t length)
623 {
624  uint_t i;
625  uint_t n;
626  size_t totalLength;
627  uint8_t *p;
628 
629  //Total number of bytes written
630  totalLength = 0;
631 
632  //Loop through data chunks
633  for(i = 0; i < dest->chunkCount && totalLength < length; i++)
634  {
635  //Is there any data to copy in the current chunk?
636  if(destOffset < dest->chunk[i].length)
637  {
638  //Point to the first byte to be written
639  p = (uint8_t *) dest->chunk[i].address + destOffset;
640  //Compute the number of bytes to copy at a time
641  n = MIN(length - totalLength, dest->chunk[i].length - destOffset);
642 
643  //Copy data
644  osMemcpy(p, src, n);
645 
646  //Advance read pointer
647  src = (uint8_t *) src + n;
648  //Total number of bytes written
649  totalLength += n;
650  //Process the next block from the start
651  destOffset = 0;
652  }
653  else
654  {
655  //Skip the current chunk
656  destOffset -= dest->chunk[i].length;
657  }
658  }
659 
660  //Return the actual number of bytes written
661  return totalLength;
662 }
663 
664 
665 /**
666  * @brief Read data from a multi-part buffer
667  * @param[out] dest Pointer to the buffer where to return the data
668  * @param[in] src Pointer to a multi-part buffer
669  * @param[in] srcOffset Offset from the beginning of the multi-part buffer
670  * @param[in] length Number of bytes to copy
671  * @return Actual number of bytes copied
672  **/
673 
674 size_t netBufferRead(void *dest, const NetBuffer *src,
675  size_t srcOffset, size_t length)
676 {
677  uint_t i;
678  uint_t n;
679  size_t totalLength;
680  uint8_t *p;
681 
682  //Total number of bytes copied
683  totalLength = 0;
684 
685  //Loop through data chunks
686  for(i = 0; i < src->chunkCount && totalLength < length; i++)
687  {
688  //Is there any data to copy from the current chunk?
689  if(srcOffset < src->chunk[i].length)
690  {
691  //Point to the first byte to be read
692  p = (uint8_t *) src->chunk[i].address + srcOffset;
693  //Compute the number of bytes to copy at a time
694  n = MIN(length - totalLength, src->chunk[i].length - srcOffset);
695 
696  //Copy data
697  osMemcpy(dest, p, n);
698 
699  //Advance write pointer
700  dest = (uint8_t *) dest + n;
701  //Total number of bytes copied
702  totalLength += n;
703  //Process the next block from the start
704  srcOffset = 0;
705  }
706  else
707  {
708  //Skip the current chunk
709  srcOffset -= src->chunk[i].length;
710  }
711  }
712 
713  //Return the actual number of bytes copied
714  return totalLength;
715 }
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
int bool_t
Definition: compiler_port.h:53
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_WARNING(...)
Definition: debug.h:85
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
uint16_t totalLength
Definition: ipv4.h:292
uint8_t p
Definition: ndp.h:300
TCP/IP stack core.
error_t memPoolInit(void)
Memory pool initialization.
Definition: net_mem.c:70
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:415
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:506
void memPoolFree(void *p)
Release a memory block.
Definition: net_mem.c:166
#define MAX_CHUNK_COUNT
Definition: net_mem.c:41
void memPoolGetStats(uint_t *currentUsage, uint_t *maxUsage, uint_t *size)
Get memory pool usage.
Definition: net_mem.c:207
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:621
NetBuffer * netBufferAlloc(size_t length)
Allocate a multi-part buffer.
Definition: net_mem.c:243
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
error_t netBufferConcat(NetBuffer *dest, const NetBuffer *src, size_t srcOffset, size_t length)
Concatenate two multi-part buffers.
Definition: net_mem.c:444
void * memPoolAlloc(size_t size)
Allocate a memory block.
Definition: net_mem.c:100
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:322
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:588
uint_t memPoolMaxUsage
Definition: net_mem.c:60
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:674
Memory management.
#define CHUNKED_BUFFER_HEADER_SIZE
Definition: net_mem.h:61
#define NET_MEM_POOL_BUFFER_SIZE
Definition: net_mem.h:55
#define NET_MEM_POOL_BUFFER_COUNT
Definition: net_mem.h:48
#define osMemset(p, value, length)
Definition: os_port.h:135
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define MIN(a, b)
Definition: os_port.h:63
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define MAX(a, b)
Definition: os_port.h:67
bool_t osCreateMutex(OsMutex *mutex)
Create a mutex object.
__weak_func void * osAllocMem(size_t size)
Allocate a memory block.
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
__weak_func void osFreeMem(void *p)
Release a previously allocated memory block.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
Structure describing a chunk of data.
Definition: net_mem.h:77
uint16_t length
Definition: net_mem.h:79
uint16_t size
Definition: net_mem.h:80
void * address
Definition: net_mem.h:78
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint_t chunkCount
Definition: net_mem.h:90
uint_t maxChunkCount
Definition: net_mem.h:91
ChunkDesc chunk[]
Definition: net_mem.h:92
Mutex object.
uint8_t length
Definition: tcp.h:368