

11 changed files with 452 additions and 1077 deletions
@ -1,299 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Lenders <mlenders@inf.fu-berlin.de> |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf |
||||
* @{ |
||||
* |
||||
* @file |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "_pktbuf_internal.h" |
||||
#include "net/ng_pktbuf.h" |
||||
#include "utlist.h" |
||||
|
||||
#if NG_PKTBUF_SIZE == 0 |
||||
/* chunk table to allow for free(ptr + x)-like behaviour */ |
||||
typedef struct __attribute__((packed)) _chunk_list_t { |
||||
struct _chunk_list_t *next; |
||||
uint8_t *ptr; |
||||
} _chunk_list_t; |
||||
|
||||
typedef struct __attribute__((packed)) _chunk_table_t { |
||||
struct _chunk_table_t *next; |
||||
uint8_t *range_start; |
||||
size_t range_len; |
||||
_chunk_list_t *chunks; |
||||
uint8_t used; |
||||
} _chunk_table_t; |
||||
|
||||
static _chunk_table_t *_chunk_table = NULL; |
||||
|
||||
/* this organizes chunks, since free(ptr + x) is not possible on most platforms */ |
||||
static _chunk_table_t *_create_table_entry(void *pkt, size_t size); |
||||
static _chunk_table_t *_find_chunk(const uint8_t *chunk, _chunk_table_t **prev, |
||||
_chunk_list_t **node_res); |
||||
static inline bool _in_range(_chunk_table_t *entry, uint8_t *ptr); |
||||
|
||||
void *_pktbuf_internal_alloc(size_t size) |
||||
{ |
||||
_chunk_table_t *entry; |
||||
void *data; |
||||
|
||||
if (size == 0) { |
||||
return 0; |
||||
} |
||||
|
||||
data = malloc(size); |
||||
|
||||
if (data == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
entry = _create_table_entry(data, size); |
||||
|
||||
if (entry == NULL) { |
||||
free(data); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
/* cppcheck-suppress memleak entry will be freed eventually in _pktbuf_internal_free().
|
||||
* Checked with valgrind. */ |
||||
return data; |
||||
} |
||||
|
||||
void *_pktbuf_internal_realloc(void *ptr, size_t size) |
||||
{ |
||||
_chunk_list_t *node = NULL; |
||||
void *new_value = NULL; |
||||
_chunk_table_t *entry; |
||||
|
||||
if (size == 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
entry = _find_chunk(ptr, NULL, &node); |
||||
|
||||
/* entry can't be NULL since prelimanary _pktbuf_internal_contains() check ensures that */ |
||||
if ((ptr == entry->range_start) && (entry->chunks == NULL)) { |
||||
new_value = realloc(entry->range_start, size); |
||||
|
||||
if (new_value == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
entry->range_start = new_value; |
||||
entry->range_len = size; |
||||
} |
||||
else { |
||||
size_t range_len = entry->range_len; |
||||
|
||||
if (node != NULL) { |
||||
range_len -= (node->ptr - entry->range_start); |
||||
} |
||||
|
||||
new_value = malloc(size); |
||||
|
||||
if (new_value == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
entry = _create_table_entry(new_value, size); |
||||
|
||||
if (entry == NULL) { |
||||
free(new_value); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
memcpy(new_value, ptr, (size < range_len) ? size : range_len); |
||||
_pktbuf_internal_free(ptr); |
||||
} |
||||
|
||||
return new_value; |
||||
/* cppcheck-suppress memleak entry will be freed eventually in _pktbuf_internal_free().
|
||||
* Checked with valgrind. */ |
||||
} |
||||
|
||||
bool _pktbuf_internal_add_pkt(void *ptr) |
||||
{ |
||||
_chunk_table_t *entry = _chunk_table; |
||||
|
||||
while (entry != NULL) { |
||||
if (_in_range(entry, ptr)) { |
||||
_chunk_list_t *node = malloc(sizeof(_chunk_list_t)); |
||||
|
||||
if (node == NULL) { |
||||
return false; |
||||
} |
||||
|
||||
node->ptr = ptr; |
||||
LL_PREPEND(entry->chunks, node); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
entry = entry->next; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
void _pktbuf_internal_free(void *ptr) |
||||
{ |
||||
_chunk_list_t *node = NULL; |
||||
_chunk_table_t *prev = NULL, *entry = _find_chunk(ptr, &prev, &node); |
||||
|
||||
if (node != NULL) { |
||||
LL_DELETE(entry->chunks, node); |
||||
free(node); |
||||
} |
||||
else if (entry->range_start == ptr) { |
||||
entry->used = 0; |
||||
} |
||||
|
||||
if (entry->chunks == NULL && entry->used == 0) { |
||||
if (prev == NULL) { |
||||
if (entry->next == NULL) { |
||||
_chunk_table = NULL; |
||||
} |
||||
else { |
||||
_chunk_table = entry->next; |
||||
} |
||||
} |
||||
else { |
||||
prev->next = entry->next; |
||||
} |
||||
|
||||
free(entry->range_start); |
||||
free(entry); |
||||
} |
||||
} |
||||
|
||||
bool _pktbuf_internal_contains(const void *ptr) |
||||
{ |
||||
return (_find_chunk(ptr, NULL, NULL) != NULL); |
||||
} |
||||
|
||||
#ifdef DEVELHELP |
||||
void _pktbuf_internal_stats(void) |
||||
{ |
||||
printf("Dynamic packet buffer\n"); |
||||
} |
||||
#endif |
||||
|
||||
#ifdef TEST_SUITES |
||||
bool _pktbuf_internal_is_empty(void) |
||||
{ |
||||
return (_chunk_table == NULL); |
||||
} |
||||
|
||||
void _pktbuf_internal_reset(void) |
||||
{ |
||||
_chunk_table_t *entry = _chunk_table; |
||||
|
||||
while (entry != NULL) { |
||||
_chunk_table_t *next = entry->next; |
||||
_chunk_list_t *node = entry->chunks; |
||||
free(entry->range_start); |
||||
|
||||
while (entry->chunks != NULL) { |
||||
LL_DELETE(entry->chunks, node); |
||||
free(node); |
||||
} |
||||
|
||||
free(entry); |
||||
entry = next; |
||||
} |
||||
|
||||
_chunk_table = NULL; |
||||
} |
||||
#endif |
||||
|
||||
static _chunk_table_t *_create_table_entry(void *data, size_t size) |
||||
{ |
||||
_chunk_table_t *entry = (_chunk_table_t *)malloc(sizeof(_chunk_table_t)); |
||||
|
||||
if (entry == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
if (_chunk_table == NULL) { |
||||
entry->next = NULL; |
||||
_chunk_table = entry; |
||||
} |
||||
else { |
||||
entry->next = _chunk_table; |
||||
_chunk_table = entry; |
||||
} |
||||
|
||||
entry->range_start = data; |
||||
entry->range_len = size; |
||||
entry->chunks = NULL; |
||||
entry->used = 1; |
||||
|
||||
return entry; |
||||
} |
||||
|
||||
static _chunk_table_t *_find_chunk(const uint8_t *chunk, _chunk_table_t **prev, |
||||
_chunk_list_t **node_res) |
||||
{ |
||||
_chunk_table_t *entry = _chunk_table; |
||||
|
||||
if (prev != NULL) { |
||||
*prev = NULL; |
||||
} |
||||
|
||||
while (entry != NULL) { |
||||
_chunk_list_t *node = entry->chunks; |
||||
|
||||
if (entry->range_start == chunk) { |
||||
if (node_res != NULL) { |
||||
*node_res = NULL; |
||||
} |
||||
|
||||
return entry; |
||||
} |
||||
|
||||
while (node != NULL) { |
||||
if (node->ptr == chunk) { |
||||
if (node_res != NULL) { |
||||
*node_res = node; |
||||
} |
||||
|
||||
return entry; |
||||
} |
||||
|
||||
node = node->next; |
||||
} |
||||
|
||||
if (prev != NULL) { |
||||
*prev = entry; |
||||
} |
||||
|
||||
entry = entry->next; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
static inline bool _in_range(_chunk_table_t *entry, uint8_t *ptr) |
||||
{ |
||||
return (entry != NULL) && |
||||
(ptr >= entry->range_start) && |
||||
(ptr < (entry->range_start + entry->range_len)); |
||||
} |
||||
#endif |
||||
|
||||
/** @} */ |
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Lenders <mlenders@inf.fu-berlin.de> |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Internal definitions for the packet buffer |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
#ifndef XPKTBUF_INTERNAL_H_ |
||||
#define XPKTBUF_INTERNAL_H_ |
||||
|
||||
#include <stdbool.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/**
|
||||
* @brief Internal alloc on packet buffer |
||||
* |
||||
* @internal |
||||
* |
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html"> |
||||
* malloc() |
||||
* </a> |
||||
*/ |
||||
void *_pktbuf_internal_alloc(size_t size); |
||||
|
||||
/**
|
||||
* @brief Internal realloc on static packet buffer |
||||
* |
||||
* @internal |
||||
* |
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html"> |
||||
* realloc() |
||||
* </a> |
||||
*/ |
||||
void *_pktbuf_internal_realloc(void *ptr, size_t size); |
||||
|
||||
/**
|
||||
* @brief Adds packet that uses @p ptr for its data part |
||||
* |
||||
* @internal |
||||
*/ |
||||
bool _pktbuf_internal_add_pkt(void *ptr); |
||||
|
||||
/**
|
||||
* @brief Internal free on static packet buffer |
||||
* |
||||
* @internal |
||||
* |
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html"> |
||||
* free() |
||||
* </a> but @p ptr can be anywhere in alloced space. |
||||
*/ |
||||
void _pktbuf_internal_free(void *ptr); |
||||
|
||||
/**
|
||||
* @brief Checks if a pointer is part of the static packet buffer. |
||||
* |
||||
* @param[in] ptr A pointer. |
||||
* |
||||
* @return true, if @p ptr is part of the static packet buffer. |
||||
* @return false, if @p ptr is not part of the static packet buffer. |
||||
*/ |
||||
bool _pktbuf_internal_contains(const void *ptr); |
||||
|
||||
#ifdef DEVELHELP |
||||
/**
|
||||
* @brief Prints some statistics about the packet buffer to stdout. |
||||
* |
||||
* @details Statistics include maximum number of reserved bytes. |
||||
*/ |
||||
void _pktbuf_internal_stats(void); |
||||
#endif |
||||
|
||||
/* for testing */ |
||||
#ifdef TEST_SUITES |
||||
/**
|
||||
* @brief Checks if packet buffer is empty |
||||
* |
||||
* @return 1, if packet buffer is empty |
||||
* @return 0, if packet buffer is not empty |
||||
*/ |
||||
bool _pktbuf_internal_is_empty(void); |
||||
|
||||
/**
|
||||
* @brief Sets the whole packet buffer to 0 |
||||
*/ |
||||
void _pktbuf_internal_reset(void); |
||||
#endif /* TEST_SUITES */ |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* XPKTBUF_INTERNAL_H_ */ |
||||
/** @} */ |
@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Lenders <mlenders@inf.fu-berlin.de> |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf |
||||
* @{ |
||||
* |
||||
* @file |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <inttypes.h> |
||||
#include <stdbool.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "od.h" |
||||
#include "net/ng_pktbuf.h" |
||||
#include "_pktbuf_internal.h" |
||||
|
||||
/* only for static packet buffer */ |
||||
#if NG_PKTBUF_SIZE > 0 |
||||
|
||||
#define _PKTBUF_ALIGN_BYTES (sizeof(void *)) |
||||
|
||||
#ifdef DEVELHELP |
||||
static unsigned int _pktbuf_max_bytes = 0; |
||||
#endif |
||||
|
||||
/**
|
||||
* @brief Data type to represent used chunks in packet buffer. |
||||
*/ |
||||
typedef struct __attribute__((packed)) _used_t { |
||||
struct _used_t *next; |
||||
uint16_t size; |
||||
uint8_t pkts; |
||||
uint8_t _align; /* alignment */ |
||||
} _used_t; |
||||
|
||||
static uint8_t _buf[NG_PKTBUF_SIZE]; |
||||
|
||||
/**
|
||||
* @brief Get first element in buffer |
||||
*/ |
||||
static inline _used_t *_head(void) |
||||
{ |
||||
return (_used_t *)_buf; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Get data part (memory behind `_used_t` descriptive header) of a packet |
||||
*/ |
||||
static inline void *_data(_used_t *node) |
||||
{ |
||||
return (void *)(((_used_t *)node) + 1); |
||||
} |
||||
|
||||
static inline void *_data_end(_used_t *node) |
||||
{ |
||||
return (void *)(((uint8_t *)_data(node)) + node->size); |
||||
} |
||||
|
||||
static inline bool _in_data_range(_used_t *node, const void *ptr) |
||||
{ |
||||
return (ptr >= _data(node)) && (ptr < _data_end(node)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Size with metadata of allocation |
||||
*/ |
||||
static inline size_t _total_sz(uint16_t sz) |
||||
{ |
||||
return sizeof(_used_t) + sz; |
||||
} |
||||
|
||||
/**
|
||||
* @brief _used_t typed alias for _total_sz |
||||
*/ |
||||
static inline size_t __total_sz(_used_t *node) |
||||
{ |
||||
return _total_sz(node->size); |
||||
} |
||||
|
||||
/**
|
||||
* @brief aligns @p size to the next word alignment. |
||||
*/ |
||||
static inline size_t _al_sz(size_t size) |
||||
{ |
||||
if (size % _PKTBUF_ALIGN_BYTES) { |
||||
return size + (_PKTBUF_ALIGN_BYTES - (size % _PKTBUF_ALIGN_BYTES)); |
||||
} |
||||
else { |
||||
return size; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief aligned size with metadata |
||||
*/ |
||||
static inline size_t __al_total_sz(_used_t *node) |
||||
{ |
||||
return _al_sz(__total_sz(node)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Index of an allocation's first byte in buffer |
||||
*/ |
||||
static inline unsigned int _start_idx(_used_t *node) |
||||
{ |
||||
return (int)(((uint8_t *)node) - _buf); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Index of an allocation's last byte in buffer |
||||
*/ |
||||
static inline unsigned int _end_idx(_used_t *node) |
||||
{ |
||||
return _start_idx(node) + __total_sz(node) - 1; |
||||
} |
||||
|
||||
static _used_t *_find(_used_t **prev_ptr, _used_t **node_ptr, const void *ptr) |
||||
{ |
||||
_used_t *node = _head(), *prev = NULL; |
||||
|
||||
if (ptr != NULL) { |
||||
while (node != NULL) { |
||||
if (_in_data_range(node, ptr)) { |
||||
*prev_ptr = prev; |
||||
*node_ptr = node; |
||||
return node; |
||||
} |
||||
|
||||
prev = node; |
||||
node = (_used_t *)node->next; |
||||
} |
||||
} |
||||
|
||||
*prev_ptr = NULL; |
||||
*node_ptr = NULL; |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Allocate chunk of @p size in _buf |
||||
*/ |
||||
void *_pktbuf_internal_alloc(size_t size) |
||||
{ |
||||
_used_t *node = _head(), *old_next, *new_next; |
||||
|
||||
if ((size == 0) || (size > NG_PKTBUF_SIZE)) { |
||||
return NULL; |
||||
} |
||||
|
||||
if (node->size == 0) { /* if head is currently not initialized */ |
||||
if (node->next == NULL || (_start_idx(node->next) >= _total_sz(size))) { |
||||
/* if enough space is there */ |
||||
node->size = size; /* just take it */ |
||||
node->pkts = 1; |
||||
|
||||
return _data(node); |
||||
} |
||||
else if (node->next != NULL) { |
||||
/* else go directly to next allocation if it exists */ |
||||
node = node->next; |
||||
} |
||||
} |
||||
|
||||
while ((node->next != NULL) /* while not last chunk allocation */ |
||||
/* and if space between current and next allocation is not big enough */ |
||||
&& ((_start_idx(node->next) - _end_idx(node)) < _al_sz(_total_sz(size)))) { |
||||
node = node->next; |
||||
} |
||||
|
||||
/* jump ahead size of current packet */ |
||||
new_next = (_used_t *)(((uint8_t *)node) + __al_total_sz(node)); |
||||
|
||||
if ((((uint8_t *)new_next) + size) > (((uint8_t *)_head()) + NG_PKTBUF_SIZE)) { |
||||
/* new packet does not fit into _pktbuf */ |
||||
return NULL; |
||||
} |
||||
|
||||
old_next = node->next; |
||||
node->next = new_next; |
||||
node->next->next = old_next; |
||||
|
||||
node = new_next; |
||||
node->size = size; |
||||
node->pkts = 1; |
||||
|
||||
#ifdef DEVELHELP |
||||
|
||||
if ((_end_idx(node) + 1) > _pktbuf_max_bytes) { |
||||
_pktbuf_max_bytes = _end_idx(node) + 1; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
return _data(node); |
||||
} |
||||
|
||||
bool _pktbuf_internal_add_pkt(void *ptr) |
||||
{ |
||||
_used_t *prev, *node; |
||||
|
||||
if (_find(&prev, &node, ptr) != NULL) { |
||||
(node->pkts)++; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
static inline void _free_helper(_used_t *prev, _used_t *node) |
||||
{ |
||||
if ((--(node->pkts)) == 0) { |
||||
if (prev == NULL) { |
||||
node->size = 0; |
||||
} |
||||
else { |
||||
prev->next = node->next; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void _pktbuf_internal_free(void *ptr) |
||||
{ |
||||
_used_t *prev, *node; |
||||
|
||||
if (_find(&prev, &node, ptr) != NULL) { |
||||
_free_helper(prev, node); |
||||
} |
||||
} |
||||
|
||||
void *_pktbuf_internal_realloc(void *ptr, size_t size) |
||||
{ |
||||
_used_t *new, *prev, *orig = NULL; |
||||
|
||||
if ((size == 0) || (size > NG_PKTBUF_SIZE)) { |
||||
return NULL; |
||||
} |
||||
|
||||
_find(&prev, &orig, ptr); |
||||
|
||||
if ((orig != NULL) && |
||||
((orig->size >= size) /* size in orig is sufficient */ |
||||
|| ((orig->next == NULL) /* or orig is last packet and buffer space is sufficient */ |
||||
&& ((_start_idx(orig) + _total_sz(size)) < NG_PKTBUF_SIZE)) |
||||
|| ((orig->next != NULL) /* or space between orig and orig->next is sufficient */ |
||||
&& ((_start_idx(orig->next) - _start_idx(orig)) >= _total_sz(size))))) { |
||||
orig->size = size; |
||||
|
||||
return ptr; |
||||
} |
||||
|
||||
new = _pktbuf_internal_alloc(size); |
||||
|
||||
if (new != NULL) { |
||||
if (orig != NULL) { |
||||
memcpy(_data(new), ptr, (orig->size < size) ? orig->size : size); |
||||
_free_helper(prev, orig); |
||||
} |
||||
|
||||
return _data(new); |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
bool _pktbuf_internal_contains(const void *ptr) |
||||
{ |
||||
return ((_buf < ((uint8_t *)ptr)) && (((uint8_t *)ptr) <= &(_buf[NG_PKTBUF_SIZE - 1]))); |
||||
} |
||||
|
||||
#ifdef DEVELHELP |
||||
void _pktbuf_internal_stats(void) |
||||
{ |
||||
_used_t *ptr = _head(); |
||||
|
||||
printf("== Static packet buffer ==\n"); |
||||
printf("--- Maximum number of reserved bytes: %u ---\n", _pktbuf_max_bytes); |
||||
while (ptr) { |
||||
printf("**** %p: next: %p, size: %" PRIu16 ", pkts: %" PRIu8 " ****\n", |
||||
(void *)ptr, (void *)ptr->next, ptr->size, ptr->pkts); |
||||
od_hex_dump(_data(ptr), ptr->size, OD_WIDTH_DEFAULT); |
||||
puts(""); |
||||
ptr = ptr->next; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
/* for testing */ |
||||
#ifdef TEST_SUITES |
||||
bool _pktbuf_internal_is_empty(void) |
||||
{ |
||||
return ((_head()->next == NULL) && (_head()->size == 0)); |
||||
} |
||||
|
||||
void _pktbuf_internal_reset(void) |
||||
{ |
||||
_head()->next = NULL; |
||||
_head()->size = 0; |
||||
#ifdef DEVELHELP |
||||
_pktbuf_max_bytes = 0; |
||||
#endif |
||||
} |
||||
#endif /* TEST_SUITES */ |
||||
#endif /* NG_PKTBUF_SIZE > 0 */ |
||||
|
||||
/** @} */ |
@ -1,350 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf |
||||
* @{ |
||||
* |
||||
* @file |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <errno.h> |
||||
#include <inttypes.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "clist.h" |
||||
#include "mutex.h" |
||||
#include "utlist.h" |
||||
#include "net/ng_pktbuf.h" |
||||
#include "net/ng_nettype.h" |
||||
#include "net/ng_pkt.h" |
||||
|
||||
#include "_pktbuf_internal.h" |
||||
|
||||
#define ENABLE_DEBUG (0) |
||||
#include "debug.h" |
||||
|
||||
static mutex_t _pktbuf_mutex = MUTEX_INIT; |
||||
|
||||
/* internal ng_pktbuf functions */ |
||||
static ng_pktsnip_t *_pktbuf_alloc(size_t size); |
||||
static ng_pktsnip_t *_pktbuf_add_unsafe(ng_pktsnip_t *pkt, void *data, |
||||
size_t size, ng_nettype_t type); |
||||
static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt); |
||||
|
||||
int ng_pktbuf_realloc_data(ng_pktsnip_t *pkt, size_t size) |
||||
{ |
||||
void *new; |
||||
|
||||
if (pkt == NULL || !_pktbuf_internal_contains(pkt->data)) { |
||||
DEBUG("pktbuf: (pkt = %p) not in packet buffer\n", (void *)pkt); |
||||
return ENOENT; |
||||
} |
||||
|
||||
if (pkt->users > 1 || pkt->next != NULL) { |
||||
DEBUG("pktbuf: more than one user (%u) or pkt->next != NULL (%p)\n", |
||||
pkt->users, (void *)pkt->next); |
||||
return EINVAL; |
||||
} |
||||
|
||||
mutex_lock(&_pktbuf_mutex); |
||||
|
||||
new = _pktbuf_internal_realloc(pkt->data, size); |
||||
|
||||
mutex_unlock(&_pktbuf_mutex); |
||||
|
||||
if (new == NULL) { |
||||
DEBUG("pktbuf: no buffer space for realloc left\n"); |
||||
return ENOMEM; |
||||
} |
||||
|
||||
pkt->data = new; |
||||
pkt->size = size; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *pkt, void *data, size_t size, |
||||
ng_nettype_t type) |
||||
{ |
||||
ng_pktsnip_t *new_pktsnip; |
||||
|
||||
mutex_lock(&_pktbuf_mutex); |
||||
|
||||
new_pktsnip = _pktbuf_add_unsafe(pkt, data, size, type); |
||||
|
||||
mutex_unlock(&_pktbuf_mutex); |
||||
|
||||
return new_pktsnip; |
||||
} |
||||
|
||||
void ng_pktbuf_hold(ng_pktsnip_t *pkt, unsigned int num) |
||||
{ |
||||
if ((pkt == NULL) || (num == 0)) { |
||||
return; |
||||
} |
||||
|
||||
mutex_lock(&_pktbuf_mutex); |
||||
|
||||
while (pkt != NULL) { |
||||
DEBUG("pktbuf: hold (pkt = %p) %u times\n", (void *)pkt, num); |
||||
pkt->users += num; |
||||
pkt = pkt->next; |
||||
} |
||||
|
||||
mutex_unlock(&_pktbuf_mutex); |
||||
} |
||||
|
||||
void ng_pktbuf_release(ng_pktsnip_t *pkt) |
||||
{ |
||||
if (pkt == NULL) { |
||||
return; |
||||
} |
||||
|
||||
mutex_lock(&_pktbuf_mutex); |
||||
|
||||
while (pkt != NULL) { |
||||
if (pkt->users > 0) { /* Don't accidentally overshoot */ |
||||
DEBUG("pktbuf: release (pkt = %p)\n", (void *)pkt); |
||||
pkt->users--; |
||||
} |
||||
|
||||
if (pkt->users == 0) { |
||||
if (_pktbuf_internal_contains(pkt->data)) { |
||||
DEBUG("pktbuf: free pkt->data = %p\n", pkt->data); |
||||
_pktbuf_internal_free(pkt->data); |
||||
} |
||||
|
||||
if (_pktbuf_internal_contains(pkt)) { |
||||
DEBUG("pktbuf: free pkt = %p\n", (void *)pkt); |
||||
_pktbuf_internal_free(pkt); |
||||
} |
||||
} |
||||
|
||||
pkt = pkt->next; |
||||
} |
||||
|
||||
mutex_unlock(&_pktbuf_mutex); |
||||
} |
||||
|
||||
ng_pktsnip_t *ng_pktbuf_start_write(ng_pktsnip_t *pkt) |
||||
{ |
||||
if (pkt != NULL && pkt->users > 1) { |
||||
ng_pktsnip_t *res = NULL; |
||||
|
||||
mutex_lock(&_pktbuf_mutex); |
||||
|
||||
DEBUG("pktbuf: pkt->users = %u => copy-on-write\n", pkt->users); |
||||
res = _pktbuf_duplicate(pkt); |
||||
DEBUG("pktbuf: copy-on-write result: (pkt = %p) copied to (res = %p)\n", |
||||
(void *)pkt, (void *)res); |
||||
|
||||
pkt->users--; |
||||
|
||||
mutex_unlock(&_pktbuf_mutex); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
return pkt; |
||||
} |
||||
|
||||
/***********************************
|
||||
* internal ng_pktbuf functions * |
||||
***********************************/ |
||||
|
||||
static ng_pktsnip_t *_pktbuf_alloc(size_t size) |
||||
{ |
||||
ng_pktsnip_t *pkt; |
||||
|
||||
pkt = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t)); |
||||
DEBUG("pktbuf: allocated (pkt = %p) ", (void *)pkt); |
||||
|
||||
if (pkt == NULL) { |
||||
DEBUG("=> failed\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("of size %u\n", (unsigned)sizeof(ng_pktsnip_t)); |
||||
|
||||
pkt->data = _pktbuf_internal_alloc(size); |
||||
DEBUG("pktbuf: allocated (pkt->data = %p) ", pkt->data); |
||||
|
||||
if (pkt->data == NULL) { |
||||
_pktbuf_internal_free(pkt); |
||||
DEBUG("=> failed (freeing %p)\n", (void *)pkt); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("of size %u\n", (unsigned)size); |
||||
pkt->next = NULL; |
||||
pkt->size = size; |
||||
pkt->users = 1; |
||||
|
||||
return pkt; |
||||
} |
||||
|
||||
static ng_pktsnip_t *_pktbuf_add_unsafe(ng_pktsnip_t *pkt, void *data, |
||||
size_t size, ng_nettype_t type) |
||||
{ |
||||
ng_pktsnip_t *new_pktsnip; |
||||
|
||||
if (pkt == NULL || pkt->data != data) { |
||||
new_pktsnip = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t)); |
||||
DEBUG("pktbuf: allocated (new_pktsnip = %p) ", (void *)new_pktsnip); |
||||
|
||||
if (new_pktsnip == NULL) { |
||||
DEBUG("=> failed\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("of size %u\n", (unsigned)sizeof(ng_pktsnip_t)); |
||||
|
||||
if ((size != 0) && (!_pktbuf_internal_contains(data))) { |
||||
new_pktsnip->data = _pktbuf_internal_alloc(size); |
||||
DEBUG("pktbuf: allocated (new_pktsnip->data = %p) ", new_pktsnip->data); |
||||
|
||||
if (new_pktsnip->data == NULL) { |
||||
_pktbuf_internal_free(new_pktsnip); |
||||
DEBUG("=> failed (freeing %p)\n", (void *)pkt); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("of size %u\n", (unsigned)size); |
||||
|
||||
if (data != NULL) { |
||||
DEBUG("pktbuf: copying %u byte from %p to %p\n", (unsigned)size, |
||||
data, new_pktsnip->data); |
||||
memcpy(new_pktsnip->data, data, size); |
||||
} |
||||
} |
||||
else { |
||||
if (_pktbuf_internal_contains(data)) { |
||||
DEBUG("pktbuf: Adding chunk to %p ", new_pktsnip->data); |
||||
|
||||
if (!_pktbuf_internal_add_pkt(new_pktsnip->data)) { |
||||
_pktbuf_internal_free(new_pktsnip); |
||||
DEBUG("failed (freeing %p)\n", (void *)pkt); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("successful\n"); |
||||
} |
||||
|
||||
new_pktsnip->data = data; |
||||
DEBUG("pktbuf: set new_pktsnip->data = %p\n", new_pktsnip->data); |
||||
} |
||||
|
||||
new_pktsnip->next = NULL; |
||||
LL_PREPEND(pkt, new_pktsnip); |
||||
DEBUG("pktbuf: prepended new_pktsnip to pkt\n"); |
||||
} |
||||
else { |
||||
if (size > pkt->size) { |
||||
DEBUG("pktbuf: new size (%u) out of pkt's boundaries (pkt->size = %u)", |
||||
(unsigned)size, (unsigned)pkt->size); |
||||
return NULL; |
||||
} |
||||
|
||||
if (size == pkt->size) { |
||||
DEBUG("pktbuf: size (%u) == pkt->size (%u) => just set new packet " |
||||
"type and return\n", (unsigned)size, (unsigned)pkt->size); |
||||
pkt->type = type; |
||||
return pkt; |
||||
} |
||||
|
||||
new_pktsnip = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t)); |
||||
DEBUG("pktbuf: allocated (new_pktsnip = %p) ", (void *)new_pktsnip); |
||||
|
||||
if (new_pktsnip == NULL) { |
||||
DEBUG("=> failed\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("of size %u\n", (unsigned)sizeof(ng_pktsnip_t)); |
||||
DEBUG("pktbuf: Adding chunk to %p ", pkt->data); |
||||
|
||||
if (!_pktbuf_internal_add_pkt(pkt->data)) { |
||||
DEBUG("failed (freeing %p)\n", (void *)new_pktsnip); |
||||
_pktbuf_internal_free(new_pktsnip); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("successful\n"); |
||||
new_pktsnip->next = pkt->next; |
||||
new_pktsnip->data = data; |
||||
DEBUG("pktbuf: set new_pktsnip->data = %p\n", new_pktsnip->data); |
||||
|
||||
DEBUG("pktbuf: add new_pktsnip (%p) to pkt (%p) after head\n", |
||||
(void *)new_pktsnip, (void *)pkt); |
||||
pkt->next = new_pktsnip; |
||||
pkt->size -= size; |
||||
DEBUG("pktbuf: resize pkt->size to %u\n", (unsigned)pkt->size); |
||||
pkt->data = (void *)(((uint8_t *)pkt->data) + size); |
||||
DEBUG("pktbuf: move pkt->data to %p\n", pkt->data); |
||||
} |
||||
|
||||
new_pktsnip->size = size; |
||||
new_pktsnip->type = type; |
||||
new_pktsnip->users = 1; |
||||
DEBUG("pktbuf: summary of new snip %p: next = %p, data = %p, size = %u, " |
||||
"type = %d, users: %u\n", (void *)new_pktsnip, (void *)new_pktsnip->next, |
||||
new_pktsnip->data, (unsigned)new_pktsnip->size, new_pktsnip->type, |
||||
new_pktsnip->users); |
||||
|
||||
return new_pktsnip; |
||||
} |
||||
|
||||
static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt) |
||||
{ |
||||
ng_pktsnip_t *res = NULL; |
||||
|
||||
res = _pktbuf_alloc(pkt->size); |
||||
|
||||
if (res == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
DEBUG("pktbuf: copying %u byte from %p to %p\n", (unsigned)pkt->size, |
||||
pkt->data, res->data); |
||||
memcpy(res->data, pkt->data, pkt->size); |
||||
res->type = pkt->type; |
||||
res->next = pkt->next; |
||||
DEBUG("pktbuf: set res->next to %p", (void *)pkt->next); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
#ifdef DEVELHELP |
||||
void ng_pktbuf_stats(void) |
||||
{ |
||||
_pktbuf_internal_stats(); |
||||
} |
||||
#endif |
||||
|
||||
#ifdef TEST_SUITES |
||||
bool ng_pktbuf_is_empty(void) |
||||
{ |
||||
return _pktbuf_internal_is_empty(); |
||||
} |
||||
|
||||
void ng_pktbuf_reset(void) |
||||
{ |
||||
_pktbuf_internal_reset(); |
||||
} |
||||
#endif |
||||
|
||||
/** @} */ |
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
* |
||||
* This file is subject to the terms and conditions of the GNU Lesser |
||||
* General Public License v2.1. See the file LICENSE in the top level |
||||
* directory for more details. |
||||
*/ |
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf |
||||
* @{ |
||||
* |
||||
* @file |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <assert.h> |
||||
#include <errno.h> |
||||
#include <inttypes.h> |
||||
#include <stdbool.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include "mutex.h" |
||||
#include "od.h" |
||||
#include "utlist.h" |
||||
#include "net/ng_pktbuf.h" |
||||
#include "net/ng_nettype.h" |
||||
#include "net/ng_pkt.h" |
||||
|
||||
#define ENABLE_DEBUG (0) |
||||
#include "debug.h" |
||||
|
||||
#define _ALIGNMENT_MASK (sizeof(void *) - 1) |
||||
|
||||
typedef struct _unused { |
||||
struct _unused *next; |
||||
unsigned int size; |
||||
} _unused_t; |
||||
|
||||
static mutex_t _mutex = MUTEX_INIT; |
||||
static uint8_t _pktbuf[NG_PKTBUF_SIZE]; |
||||
static _unused_t *_first_unused; |
||||
|
||||
/* internal ng_pktbuf functions */ |
||||
static ng_pktsnip_t *_create_snip(ng_pktsnip_t *next, void *data, size_t size, |
||||
ng_nettype_t type); |
||||
static void *_pktbuf_alloc(size_t size); |
||||
static void _pktbuf_free(void *data, size_t size); |
||||
|
||||
static inline bool _pktbuf_contains(void *ptr) |
||||
{ |
||||
return (&_pktbuf[0] <= (uint8_t *)ptr) && |
||||
((uint8_t *)ptr <= &_pktbuf[NG_PKTBUF_SIZE - 1]); |
||||
} |
||||
|
||||
/* fits size to byte alignment */ |
||||
static inline size_t _align(size_t size) |
||||
{ |
||||
return ((size + _ALIGNMENT_MASK) & ~(_ALIGNMENT_MASK)); |
||||
} |
||||
|
||||
|
||||
void ng_pktbuf_init(void) |
||||
{ |
||||
mutex_lock(&_mutex); |
||||
_first_unused = (_unused_t *)_pktbuf; |
||||
_first_unused->next = NULL; |
||||
_first_unused->size = sizeof(_pktbuf); |
||||
mutex_unlock(&_mutex); |
||||
} |
||||
|
||||
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *next, void *data, size_t size, |
||||
ng_nettype_t type) |
||||
{ |
||||
ng_pktsnip_t *pkt; |
||||
if ((size == 0) || (size > NG_PKTBUF_SIZE)) { |
||||
DEBUG("pktbuf: size (%u) == 0 || size == NG_PKTBUF_SIZE (%u)\n", |
||||
(unsigned)size, NG_PKTBUF_SIZE); |
||||
return NULL; |
||||
} |
||||
mutex_lock(&_mutex); |
||||
pkt = _create_snip(next, data, size, type); |
||||
mutex_unlock(&_mutex); |
||||
return pkt; |
||||
} |
||||
|
||||
ng_pktsnip_t *ng_pktbuf_mark(ng_pktsnip_t *pkt, size_t size, ng_nettype_t type) |
||||
{ |
||||
ng_pktsnip_t *marked_snip; |
||||
/* size required for chunk */ |
||||
size_t required_new_size = (size < sizeof(_unused_t)) ? |
||||
_align(sizeof(_unused_t)) : _align(size); |
||||
mutex_lock(&_mutex); |
||||
if ((size == 0) || (pkt == NULL) || (size > pkt->size) || (pkt->data == NULL)) { |
||||
DEBUG("pktbuf: size == 0 (was %u) or pkt == NULL (was %p) or " |
||||
"size > pkt->size (was %u) or pkt->data == NULL (was %p)\n", |
||||
(unsigned)size, (void *)pkt, (unsigned)pkt->size, pkt->data); |
||||
mutex_unlock(&_mutex); |
||||
return NULL; |
||||
} |
||||
else if (size == pkt->size) { |
||||
pkt->type = type; |
||||
mutex_unlock(&_mutex); |
||||
return pkt; |
||||
} |
||||
marked_snip = _pktbuf_alloc(sizeof(ng_pktsnip_t)); |
||||
if (marked_snip == NULL) { |
||||
DEBUG("pktbuf: could not reallocate marked section.\n"); |
||||
mutex_unlock(&_mutex); |
||||
return NULL; |
||||
} |
||||
if ((size < required_new_size)) { /* would not fit unused marker => move data around */ |
||||
void *new_data_marked, *new_data_rest; |
||||
new_data_marked = _pktbuf_alloc(size); |
||||
if (new_data_marked == NULL) { |
||||
DEBUG("pktbuf: could not reallocate marked section.\n"); |
||||
_pktbuf_free(marked_snip, sizeof(ng_pktsnip_t)); |
||||
mutex_unlock(&_mutex); |
||||
return NULL; |
||||
} |
||||
new_data_rest = _pktbuf_alloc(pkt->size - size); |
||||
if (new_data_rest == NULL) { |
||||
DEBUG("pktbuf: could not reallocate remaining section.\n"); |
||||
_pktbuf_free(marked_snip, sizeof(ng_pktsnip_t)); |
||||
_pktbuf_free(new_data_marked, size); |
||||
mutex_unlock(&_mutex); |
||||
return NULL; |
||||
} |
||||
memcpy(new_data_marked, pkt->data, size); |
||||
memcpy(new_data_rest, ((uint8_t *)pkt->data) + size, pkt->size - size); |
||||
_pktbuf_free(pkt->data, pkt->size); |
||||
marked_snip->data = new_data_marked; |
||||
pkt->data = new_data_rest; |
||||
} |
||||
else { |
||||
marked_snip->data = pkt->data; |
||||
pkt->data = ((uint8_t *)pkt->data) + size; |
||||
} |
||||
pkt->size -= size; |
||||
marked_snip->next = pkt->next; |
||||
marked_snip->size = size; |
||||
marked_snip->type = type; |
||||
marked_snip->users = 1; |
||||
pkt->next = marked_snip; |
||||
mutex_unlock(&_mutex); |
||||
return marked_snip; |
||||
} |
||||
|
||||
int ng_pktbuf_realloc_data(ng_pktsnip_t *pkt, size_t size) |
||||
{ |
||||
size_t aligned_size = (size < sizeof(_unused_t)) ? |
||||
_align(sizeof(_unused_t)) : _align(size); |
||||
mutex_lock(&_mutex); |
||||
assert((pkt != NULL) && (pkt->data != NULL) && _pktbuf_contains(pkt->data)); |
||||
if (size == 0) { |
||||
DEBUG("pktbuf: size == 0\n"); |
||||
mutex_unlock(&_mutex); |
||||
return ENOMEM; |
||||
} |
||||
if (size == pkt->size) { |
||||
mutex_unlock(&_mutex); |
||||
return 0; |
||||
} |
||||
if ((size > pkt->size) || /* new size does not fit */ |
||||
((pkt->size - aligned_size) < sizeof(_unused_t))) { /* resulting hole would not fit marker */ |
||||
void *new_data = _pktbuf_alloc(size); |
||||
if (new_data == NULL) { |
||||
DEBUG("pktbuf: error allocating new data section\n"); |
||||
mutex_unlock(&_mutex); |
||||
return ENOMEM; |
||||
} |
||||
memcpy(new_data, pkt->data, (pkt->size < size) ? pkt->size : size); |
||||
_pktbuf_free(pkt->data, pkt->size); |
||||
pkt->data = new_data; |
||||
} |
||||
else { |
||||
_pktbuf_free(((uint8_t *)pkt->data) + aligned_size, |
||||
pkt->size - aligned_size); |
||||
} |
||||
pkt->size = size; |
||||
mutex_unlock(&_mutex); |
||||
return 0; |
||||
} |
||||
|
||||
void ng_pktbuf_hold(ng_pktsnip_t *pkt, unsigned int num) |
||||
{ |
||||
mutex_lock(&_mutex); |
||||
while (pkt) { |
||||
pkt->users += num; |
||||
pkt = pkt->next; |
||||
} |
||||
mutex_unlock(&_mutex); |
||||
} |
||||
|
||||
void ng_pktbuf_release(ng_pktsnip_t *pkt) |
||||
{ |
||||
mutex_lock(&_mutex); |
||||
while (pkt) { |
||||
ng_pktsnip_t *tmp = pkt->next; |
||||
if (pkt->users == 1) { |
||||
pkt->users = 0; /* not necessary but to be on the safe side */ |
||||
_pktbuf_free(pkt->data, pkt->size); |
||||
_pktbuf_free(pkt, sizeof(ng_pktsnip_t)); |
||||
} |
||||
else { |
||||
pkt->users--; |
||||
} |
||||
pkt = tmp; |
||||
} |
||||
mutex_unlock(&_mutex); |
||||
} |
||||
|
||||
ng_pktsnip_t *ng_pktbuf_start_write(ng_pktsnip_t *pkt) |
||||
{ |
||||
mutex_lock(&_mutex); |
||||
if ((pkt == NULL) || (pkt->size == 0)) { |
||||
mutex_unlock(&_mutex); |
||||
return NULL; |
||||
} |
||||
if (pkt->users > 1) { |
||||
ng_pktsnip_t *new; |
||||
new = _create_snip(pkt->next, pkt->data, pkt->size, pkt->type); |
||||
if (new != NULL) { |
||||
pkt->users--; |
||||
} |
||||
mutex_unlock(&_mutex); |
||||
return new; |
||||
} |
||||
mutex_unlock(&_mutex); |
||||
return pkt; |
||||
} |
||||
|
||||
#ifdef DEVELHELP |
||||
#ifdef MODULE_OD |
||||
void _print_chunk(void *chunk, size_t size, int num) |
||||
{ |
||||
printf("================ chunk %3d (size: %4u) ================\n", num, |
||||
(unsigned int)size); |
||||
od(chunk, NG_PKTBUF_SIZE, OD_WIDTH_DEFAULT, |
||||
OD_FLAGS_ADDRESS_HEX | OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_1); |
||||
} |
||||
|
||||
void _print_unused(_unused_t *ptr) |
||||
{ |
||||
printf("~ unused: %p (next: %p, size: %4u) ~\n", (void *)ptr, |
||||
(void *)ptr->next, ptr->size); |
||||
} |
||||
#endif |
||||
|
||||
void ng_pktbuf_stats(void) |
||||
{ |
||||
#ifdef MODULE_OD |
||||
_unused_t *ptr = _first_unused; |
||||
uint8_t *chunk = &_pktbuf[0]; |
||||
int count = 0; |
||||
|
||||
printf("packet buffer: first byte: %p, last byte: %p (size: %u)\n", |
||||
(void *)&_pktbuf[0], (void *)&_pktbuf[NG_PKTBUF_SIZE], NG_PKTBUF_SIZE); |
||||
if (ptr == NULL) { /* packet buffer is completely full */ |
||||
_print_chunk(chunk, NG_PKTBUF_SIZE, count++); |
||||
} |
||||
|
||||