dynamic loading of network stack no longer needed
This commit is contained in:
408
ext/picotcp/stack/pico_device.c
Normal file
408
ext/picotcp/stack/pico_device.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_device.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_tree.h"
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_ipv4.h"
|
||||
#include "pico_icmp6.h"
|
||||
#include "pico_eth.h"
|
||||
#define PICO_DEVICE_DEFAULT_MTU (1500)
|
||||
|
||||
struct pico_devices_rr_info {
|
||||
struct pico_tree_node *node_in, *node_out;
|
||||
};
|
||||
|
||||
static struct pico_devices_rr_info Devices_rr_info = {
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
static int pico_dev_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_device *a = ka, *b = kb;
|
||||
if (a->hash < b->hash)
|
||||
return -1;
|
||||
|
||||
if (a->hash > b->hash)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_TREE_DECLARE(Device_tree, pico_dev_cmp);
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
static void device_init_ipv6_final(struct pico_device *dev, struct pico_ip6 *linklocal)
|
||||
{
|
||||
dev->hostvars.basetime = PICO_ND_REACHABLE_TIME;
|
||||
/* RFC 4861 $6.3.2 value between 0.5 and 1.5 times basetime */
|
||||
dev->hostvars.reachabletime = ((5 + (pico_rand() % 10)) * PICO_ND_REACHABLE_TIME) / 10;
|
||||
dev->hostvars.retranstime = PICO_ND_RETRANS_TIMER;
|
||||
pico_icmp6_router_solicitation(dev, linklocal);
|
||||
dev->hostvars.hoplimit = PICO_IPV6_DEFAULT_HOP;
|
||||
}
|
||||
|
||||
struct pico_ipv6_link *pico_ipv6_link_add_local(struct pico_device *dev, const struct pico_ip6 *prefix)
|
||||
{
|
||||
struct pico_ip6 newaddr;
|
||||
struct pico_ip6 netmask64 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
struct pico_ipv6_link *link;
|
||||
memcpy(newaddr.addr, prefix->addr, PICO_SIZE_IP6);
|
||||
/* modified EUI-64 + invert universal/local bit */
|
||||
newaddr.addr[8] = (dev->eth->mac.addr[0] ^ 0x02);
|
||||
newaddr.addr[9] = dev->eth->mac.addr[1];
|
||||
newaddr.addr[10] = dev->eth->mac.addr[2];
|
||||
newaddr.addr[11] = 0xff;
|
||||
newaddr.addr[12] = 0xfe;
|
||||
newaddr.addr[13] = dev->eth->mac.addr[3];
|
||||
newaddr.addr[14] = dev->eth->mac.addr[4];
|
||||
newaddr.addr[15] = dev->eth->mac.addr[5];
|
||||
link = pico_ipv6_link_add(dev, newaddr, netmask64);
|
||||
if (link) {
|
||||
device_init_ipv6_final(dev, &newaddr);
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int device_init_mac(struct pico_device *dev, uint8_t *mac)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
|
||||
#endif
|
||||
dev->eth = PICO_ZALLOC(sizeof(struct pico_ethdev));
|
||||
if (dev->eth) {
|
||||
memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH);
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
if (pico_ipv6_link_add_local(dev, &linklocal) == NULL) {
|
||||
PICO_FREE(dev->q_in);
|
||||
PICO_FREE(dev->q_out);
|
||||
PICO_FREE(dev->eth);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
} else {
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pico_device_ipv6_random_ll(struct pico_device *dev)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
|
||||
struct pico_ip6 netmask6 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
uint32_t len = (uint32_t)strlen(dev->name);
|
||||
if (strcmp(dev->name, "loop")) {
|
||||
do {
|
||||
/* privacy extension + unset universal/local and individual/group bit */
|
||||
len = pico_rand();
|
||||
linklocal.addr[8] = (uint8_t)((len & 0xffu) & (uint8_t)(~0x03));
|
||||
linklocal.addr[9] = (uint8_t)(len >> 8);
|
||||
linklocal.addr[10] = (uint8_t)(len >> 16);
|
||||
linklocal.addr[11] = (uint8_t)(len >> 24);
|
||||
len = pico_rand();
|
||||
linklocal.addr[12] = (uint8_t)len;
|
||||
linklocal.addr[13] = (uint8_t)(len >> 8);
|
||||
linklocal.addr[14] = (uint8_t)(len >> 16);
|
||||
linklocal.addr[15] = (uint8_t)(len >> 24);
|
||||
pico_rand_feed(dev->hash);
|
||||
} while (pico_ipv6_link_get(&linklocal));
|
||||
|
||||
if (pico_ipv6_link_add(dev, linklocal, netmask6) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_init_nomac(struct pico_device *dev)
|
||||
{
|
||||
if (pico_device_ipv6_random_ll(dev) < 0) {
|
||||
PICO_FREE(dev->q_in);
|
||||
PICO_FREE(dev->q_out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->eth = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac)
|
||||
{
|
||||
uint32_t len = (uint32_t)strlen(name);
|
||||
int ret = 0;
|
||||
if(len > MAX_DEVICE_NAME)
|
||||
len = MAX_DEVICE_NAME;
|
||||
|
||||
memcpy(dev->name, name, len);
|
||||
dev->hash = pico_hash(dev->name, len);
|
||||
|
||||
Devices_rr_info.node_in = NULL;
|
||||
Devices_rr_info.node_out = NULL;
|
||||
dev->q_in = PICO_ZALLOC(sizeof(struct pico_queue));
|
||||
if (!dev->q_in)
|
||||
return -1;
|
||||
|
||||
dev->q_out = PICO_ZALLOC(sizeof(struct pico_queue));
|
||||
if (!dev->q_out) {
|
||||
PICO_FREE(dev->q_in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_tree_insert(&Device_tree, dev);
|
||||
if (!dev->mtu)
|
||||
dev->mtu = PICO_DEVICE_DEFAULT_MTU;
|
||||
|
||||
if (mac) {
|
||||
ret = device_init_mac(dev, mac);
|
||||
} else {
|
||||
ret = device_init_nomac(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pico_queue_destroy(struct pico_queue *q)
|
||||
{
|
||||
if (q) {
|
||||
pico_queue_empty(q);
|
||||
PICO_FREE(q);
|
||||
}
|
||||
}
|
||||
|
||||
void pico_device_destroy(struct pico_device *dev)
|
||||
{
|
||||
|
||||
pico_queue_destroy(dev->q_in);
|
||||
pico_queue_destroy(dev->q_out);
|
||||
|
||||
if (dev->eth)
|
||||
PICO_FREE(dev->eth);
|
||||
|
||||
#ifdef PICO_SUPPORT_IPV4
|
||||
pico_ipv4_cleanup_links(dev);
|
||||
#endif
|
||||
#ifdef PICO_SUPPORT_IPV6
|
||||
pico_ipv6_cleanup_links(dev);
|
||||
#endif
|
||||
pico_tree_delete(&Device_tree, dev);
|
||||
|
||||
if (dev->destroy)
|
||||
dev->destroy(dev);
|
||||
|
||||
Devices_rr_info.node_in = NULL;
|
||||
Devices_rr_info.node_out = NULL;
|
||||
PICO_FREE(dev);
|
||||
}
|
||||
|
||||
static int check_dev_serve_interrupt(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
if ((dev->__serving_interrupt) && (dev->dsr)) {
|
||||
/* call dsr routine */
|
||||
loop_score = dev->dsr(dev, loop_score);
|
||||
}
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static int check_dev_serve_polling(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
if (dev->poll) {
|
||||
loop_score = dev->poll(dev, loop_score);
|
||||
}
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static int devloop_in(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_frame *f;
|
||||
while(loop_score > 0) {
|
||||
if (dev->q_in->frames == 0)
|
||||
break;
|
||||
|
||||
/* Receive */
|
||||
f = pico_dequeue(dev->q_in);
|
||||
if (f) {
|
||||
if (dev->eth) {
|
||||
f->datalink_hdr = f->buffer;
|
||||
(void)pico_ethernet_receive(f);
|
||||
} else {
|
||||
f->net_hdr = f->buffer;
|
||||
pico_network_receive(f);
|
||||
}
|
||||
|
||||
loop_score--;
|
||||
}
|
||||
}
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static int devloop_sendto_dev(struct pico_device *dev, struct pico_frame *f)
|
||||
{
|
||||
|
||||
if (dev->eth) {
|
||||
/* Ethernet: pass management of the frame to the pico_ethernet_send() rdv function */
|
||||
return pico_ethernet_send(f);
|
||||
} else {
|
||||
/* non-ethernet: no post-processing needed */
|
||||
return (dev->send(dev, f->start, (int)f->len) <= 0); /* Return 0 upon success, which is dev->send() > 0 */
|
||||
}
|
||||
}
|
||||
|
||||
static int devloop_out(struct pico_device *dev, int loop_score)
|
||||
{
|
||||
struct pico_frame *f;
|
||||
while(loop_score > 0) {
|
||||
if (dev->q_out->frames == 0)
|
||||
break;
|
||||
|
||||
/* Device dequeue + send */
|
||||
f = pico_queue_peek(dev->q_out);
|
||||
if (!f)
|
||||
break;
|
||||
|
||||
if (devloop_sendto_dev(dev, f) == 0) { /* success. */
|
||||
f = pico_dequeue(dev->q_out);
|
||||
pico_frame_discard(f); /* SINGLE POINT OF DISCARD for OUTGOING FRAMES */
|
||||
loop_score--;
|
||||
} else
|
||||
break; /* Don't discard */
|
||||
|
||||
}
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static int devloop(struct pico_device *dev, int loop_score, int direction)
|
||||
{
|
||||
/* If device supports interrupts, read the value of the condition and trigger the dsr */
|
||||
loop_score = check_dev_serve_interrupt(dev, loop_score);
|
||||
|
||||
/* If device supports polling, give control. Loop score is managed internally,
|
||||
* remaining loop points are returned. */
|
||||
loop_score = check_dev_serve_polling(dev, loop_score);
|
||||
|
||||
if (direction == PICO_LOOP_DIR_OUT)
|
||||
loop_score = devloop_out(dev, loop_score);
|
||||
else
|
||||
loop_score = devloop_in(dev, loop_score);
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
|
||||
static struct pico_tree_node *pico_dev_roundrobin_start(int direction)
|
||||
{
|
||||
if (Devices_rr_info.node_in == NULL)
|
||||
Devices_rr_info.node_in = pico_tree_firstNode(Device_tree.root);
|
||||
|
||||
if (Devices_rr_info.node_out == NULL)
|
||||
Devices_rr_info.node_out = pico_tree_firstNode(Device_tree.root);
|
||||
|
||||
if (direction == PICO_LOOP_DIR_IN)
|
||||
return Devices_rr_info.node_in;
|
||||
else
|
||||
return Devices_rr_info.node_out;
|
||||
}
|
||||
|
||||
static void pico_dev_roundrobin_end(int direction, struct pico_tree_node *last)
|
||||
{
|
||||
if (direction == PICO_LOOP_DIR_IN)
|
||||
Devices_rr_info.node_in = last;
|
||||
else
|
||||
Devices_rr_info.node_out = last;
|
||||
}
|
||||
|
||||
#define DEV_LOOP_MIN 16
|
||||
|
||||
int pico_devices_loop(int loop_score, int direction)
|
||||
{
|
||||
struct pico_device *start, *next;
|
||||
struct pico_tree_node *next_node = pico_dev_roundrobin_start(direction);
|
||||
|
||||
if (!next_node)
|
||||
return loop_score;
|
||||
|
||||
next = next_node->keyValue;
|
||||
start = next;
|
||||
|
||||
/* round-robin all devices, break if traversed all devices */
|
||||
while ((loop_score > DEV_LOOP_MIN) && (next != NULL)) {
|
||||
loop_score = devloop(next, loop_score, direction);
|
||||
next_node = pico_tree_next(next_node);
|
||||
next = next_node->keyValue;
|
||||
if (next == NULL)
|
||||
{
|
||||
next_node = pico_tree_firstNode(Device_tree.root);
|
||||
next = next_node->keyValue;
|
||||
}
|
||||
|
||||
if (next == start)
|
||||
break;
|
||||
}
|
||||
pico_dev_roundrobin_end(direction, next_node);
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
struct pico_device *pico_get_device(const char*name)
|
||||
{
|
||||
struct pico_device *dev;
|
||||
struct pico_tree_node *index;
|
||||
pico_tree_foreach(index, &Device_tree){
|
||||
dev = index->keyValue;
|
||||
if(strcmp(name, dev->name) == 0)
|
||||
return dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t pico_device_broadcast(struct pico_frame *f)
|
||||
{
|
||||
struct pico_tree_node *index;
|
||||
int32_t ret = -1;
|
||||
|
||||
pico_tree_foreach(index, &Device_tree)
|
||||
{
|
||||
struct pico_device *dev = index->keyValue;
|
||||
if(dev != f->dev)
|
||||
{
|
||||
struct pico_frame *copy = pico_frame_copy(f);
|
||||
|
||||
if(!copy)
|
||||
break;
|
||||
|
||||
copy->dev = dev;
|
||||
copy->dev->send(copy->dev, copy->start, (int)copy->len);
|
||||
pico_frame_discard(copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = f->dev->send(f->dev, f->start, (int)f->len);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pico_device_link_state(struct pico_device *dev)
|
||||
{
|
||||
if (!dev->link_state)
|
||||
return 1; /* Not supported, assuming link is always up */
|
||||
|
||||
return dev->link_state(dev);
|
||||
}
|
||||
286
ext/picotcp/stack/pico_frame.c
Normal file
286
ext/picotcp/stack/pico_frame.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_config.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_stack.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_DEBUG_MEMORY
|
||||
static int n_frames_allocated;
|
||||
#endif
|
||||
|
||||
/** frame alloc/dealloc/copy **/
|
||||
void pico_frame_discard(struct pico_frame *f)
|
||||
{
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
(*f->usage_count)--;
|
||||
if (*f->usage_count == 0) {
|
||||
if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER)
|
||||
PICO_FREE(f->usage_count);
|
||||
|
||||
#ifdef PICO_SUPPORT_DEBUG_MEMORY
|
||||
dbg("Discarded buffer @%p, caller: %p\n", f->buffer, __builtin_return_address(3));
|
||||
dbg("DEBUG MEMORY: %d frames in use.\n", --n_frames_allocated);
|
||||
#endif
|
||||
if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER))
|
||||
PICO_FREE(f->buffer);
|
||||
else if (f->notify_free)
|
||||
f->notify_free(f->buffer);
|
||||
|
||||
if (f->info)
|
||||
PICO_FREE(f->info);
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_DEBUG_MEMORY
|
||||
else {
|
||||
dbg("Removed frame @%p(copy), usage count now: %d\n", f, *f->usage_count);
|
||||
}
|
||||
#endif
|
||||
PICO_FREE(f);
|
||||
}
|
||||
|
||||
struct pico_frame *pico_frame_copy(struct pico_frame *f)
|
||||
{
|
||||
struct pico_frame *new = PICO_ZALLOC(sizeof(struct pico_frame));
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
memcpy(new, f, sizeof(struct pico_frame));
|
||||
*(new->usage_count) += 1;
|
||||
#ifdef PICO_SUPPORT_DEBUG_MEMORY
|
||||
dbg("Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count);
|
||||
#endif
|
||||
new->next = NULL;
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy, int ext_buffer)
|
||||
{
|
||||
struct pico_frame *p = PICO_ZALLOC(sizeof(struct pico_frame));
|
||||
uint32_t frame_buffer_size = size;
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
if (ext_buffer && !zerocopy) {
|
||||
/* external buffer implies zerocopy flag! */
|
||||
PICO_FREE(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!zerocopy) {
|
||||
unsigned int align = size % sizeof(uint32_t);
|
||||
/* Ensure that usage_count starts on an aligned address */
|
||||
if (align) {
|
||||
frame_buffer_size += (uint32_t)sizeof(uint32_t) - align;
|
||||
}
|
||||
|
||||
p->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t));
|
||||
if (!p->buffer) {
|
||||
PICO_FREE(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->usage_count = (uint32_t *)((void *)(((uint8_t*)p->buffer) + frame_buffer_size));
|
||||
} else {
|
||||
p->buffer = NULL;
|
||||
p->flags |= PICO_FRAME_FLAG_EXT_USAGE_COUNTER;
|
||||
p->usage_count = PICO_ZALLOC(sizeof(uint32_t));
|
||||
if (!p->usage_count) {
|
||||
PICO_FREE(p);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
p->buffer_len = size;
|
||||
|
||||
/* By default, frame content is the full buffer. */
|
||||
p->start = p->buffer;
|
||||
p->len = p->buffer_len;
|
||||
*p->usage_count = 1;
|
||||
|
||||
if (ext_buffer)
|
||||
p->flags |= PICO_FRAME_FLAG_EXT_BUFFER;
|
||||
|
||||
#ifdef PICO_SUPPORT_DEBUG_MEMORY
|
||||
dbg("Allocated buffer @%p, len= %d caller: %p\n", p->buffer, p->buffer_len, __builtin_return_address(2));
|
||||
dbg("DEBUG MEMORY: %d frames in use.\n", ++n_frames_allocated);
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
struct pico_frame *pico_frame_alloc(uint32_t size)
|
||||
{
|
||||
return pico_frame_do_alloc(size, 0, 0);
|
||||
}
|
||||
|
||||
int pico_frame_grow(struct pico_frame *f, uint32_t size)
|
||||
{
|
||||
uint8_t *oldbuf;
|
||||
uint32_t usage_count, *p_old_usage;
|
||||
uint32_t frame_buffer_size;
|
||||
uint32_t oldsize;
|
||||
unsigned int align;
|
||||
int addr_diff = 0;
|
||||
|
||||
if (!f || (size < f->buffer_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
align = size % sizeof(uint32_t);
|
||||
frame_buffer_size = size;
|
||||
if (align) {
|
||||
frame_buffer_size += (uint32_t)sizeof(uint32_t) - align;
|
||||
}
|
||||
|
||||
oldbuf = f->buffer;
|
||||
oldsize = f->buffer_len;
|
||||
usage_count = *(f->usage_count);
|
||||
p_old_usage = f->usage_count;
|
||||
f->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t));
|
||||
if (!f->buffer) {
|
||||
f->buffer = oldbuf;
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->usage_count = (uint32_t *)((void *)(((uint8_t*)f->buffer) + frame_buffer_size));
|
||||
*f->usage_count = usage_count;
|
||||
f->buffer_len = size;
|
||||
memcpy(f->buffer, oldbuf, oldsize);
|
||||
|
||||
/* Update hdr fields to new buffer*/
|
||||
addr_diff = (int)(f->buffer - oldbuf);
|
||||
f->net_hdr += addr_diff;
|
||||
f->datalink_hdr += addr_diff;
|
||||
f->transport_hdr += addr_diff;
|
||||
f->app_hdr += addr_diff;
|
||||
f->start += addr_diff;
|
||||
f->payload += addr_diff;
|
||||
|
||||
if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER)
|
||||
PICO_FREE(p_old_usage);
|
||||
|
||||
if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER))
|
||||
PICO_FREE(oldbuf);
|
||||
else if (f->notify_free)
|
||||
f->notify_free(oldbuf);
|
||||
|
||||
f->flags = 0;
|
||||
/* Now, the frame is not zerocopy anymore, and the usage counter has been moved within it */
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pico_frame *pico_frame_alloc_skeleton(uint32_t size, int ext_buffer)
|
||||
{
|
||||
return pico_frame_do_alloc(size, 1, ext_buffer);
|
||||
}
|
||||
|
||||
int pico_frame_skeleton_set_buffer(struct pico_frame *f, void *buf)
|
||||
{
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
f->buffer = (uint8_t *) buf;
|
||||
f->start = f->buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pico_frame *pico_frame_deepcopy(struct pico_frame *f)
|
||||
{
|
||||
struct pico_frame *new = pico_frame_alloc(f->buffer_len);
|
||||
int addr_diff;
|
||||
unsigned char *buf;
|
||||
uint32_t *uc;
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
/* Save the two key pointers... */
|
||||
buf = new->buffer;
|
||||
uc = new->usage_count;
|
||||
|
||||
/* Overwrite all fields with originals */
|
||||
memcpy(new, f, sizeof(struct pico_frame));
|
||||
|
||||
/* ...restore the two key pointers */
|
||||
new->buffer = buf;
|
||||
new->usage_count = uc;
|
||||
|
||||
/* Update in-buffer pointers with offset */
|
||||
addr_diff = (int)(new->buffer - f->buffer);
|
||||
new->datalink_hdr += addr_diff;
|
||||
new->net_hdr += addr_diff;
|
||||
new->transport_hdr += addr_diff;
|
||||
new->app_hdr += addr_diff;
|
||||
new->start += addr_diff;
|
||||
new->payload += addr_diff;
|
||||
|
||||
#ifdef PICO_SUPPORT_DEBUG_MEMORY
|
||||
dbg("Deep-Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count);
|
||||
#endif
|
||||
new->next = NULL;
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t len)
|
||||
{
|
||||
uint16_t *buf = (uint16_t *)data;
|
||||
uint16_t *stop;
|
||||
|
||||
if (len & 0x01) {
|
||||
--len;
|
||||
#ifdef PICO_BIGENDIAN
|
||||
sum += (((uint8_t *)data)[len]) << 8;
|
||||
#else
|
||||
sum += ((uint8_t *)data)[len];
|
||||
#endif
|
||||
}
|
||||
|
||||
stop = (uint16_t *)((void *)(((uint8_t *)data) + len));
|
||||
|
||||
while (buf < stop) {
|
||||
sum += *buf++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline uint16_t pico_checksum_finalize(uint32_t sum)
|
||||
{
|
||||
while (sum >> 16) { /* a second carry is possible! */
|
||||
sum = (sum & 0x0000FFFF) + (sum >> 16);
|
||||
}
|
||||
return short_be((uint16_t) ~sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate checksum of a given string
|
||||
*/
|
||||
uint16_t pico_checksum(void *inbuf, uint32_t len)
|
||||
{
|
||||
uint32_t sum;
|
||||
|
||||
sum = pico_checksum_adder(0, inbuf, len);
|
||||
return pico_checksum_finalize(sum);
|
||||
}
|
||||
|
||||
/* WARNING: len1 MUST be an EVEN number */
|
||||
uint16_t pico_dualbuffer_checksum(void *inbuf1, uint32_t len1, void *inbuf2, uint32_t len2)
|
||||
{
|
||||
uint32_t sum;
|
||||
|
||||
sum = pico_checksum_adder(0, inbuf1, len1);
|
||||
sum = pico_checksum_adder(sum, inbuf2, len2);
|
||||
return pico_checksum_finalize(sum);
|
||||
}
|
||||
|
||||
43
ext/picotcp/stack/pico_md5.c
Normal file
43
ext/picotcp/stack/pico_md5.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*********************************************************************
|
||||
* PicoTCP. Copyright (c) 2015 Altran Intelligent Systems. Some rights reserved.
|
||||
* See LICENSE and COPYING for usage.
|
||||
*
|
||||
* Authors: Daniele Lacamera
|
||||
* *********************************************************************/
|
||||
|
||||
|
||||
#include <pico_md5.h>
|
||||
|
||||
#if defined (PICO_SUPPORT_CYASSL)
|
||||
#include <cyassl/ctaocrypt/md5.h>
|
||||
|
||||
void pico_md5sum(uint8_t *dst, const uint8_t *src, size_t len)
|
||||
{
|
||||
Md5 md5;
|
||||
InitMd5(&md5);
|
||||
Md5Update(&md5, src, len);
|
||||
Md5Final(&md5, dst);
|
||||
}
|
||||
|
||||
#elif defined (PICO_SUPPORT_POLARSSL)
|
||||
#include <polarssl/md5.h>
|
||||
|
||||
void pico_md5sum(uint8_t *dst, const uint8_t *src, size_t len)
|
||||
{
|
||||
md5(src, len, dst);
|
||||
}
|
||||
|
||||
#else
|
||||
static void (*do_pico_md5sum)(uint8_t *dst, const uint8_t *src, size_t len);
|
||||
void pico_md5sum(uint8_t *dst, const uint8_t *src, size_t len)
|
||||
{
|
||||
if (do_pico_md5sum) {
|
||||
do_pico_md5sum(dst, src, len);
|
||||
}
|
||||
}
|
||||
|
||||
void pico_register_md5sum(void (*md5)(uint8_t *, const uint8_t *, size_t))
|
||||
{
|
||||
do_pico_md5sum = md5;
|
||||
}
|
||||
#endif
|
||||
214
ext/picotcp/stack/pico_protocol.c
Normal file
214
ext/picotcp/stack/pico_protocol.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
.
|
||||
|
||||
Authors: Daniele Lacamera
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_tree.h"
|
||||
|
||||
struct pico_proto_rr
|
||||
{
|
||||
struct pico_tree *t;
|
||||
struct pico_tree_node *node_in, *node_out;
|
||||
};
|
||||
|
||||
|
||||
static int pico_proto_cmp(void *ka, void *kb)
|
||||
{
|
||||
struct pico_protocol *a = ka, *b = kb;
|
||||
if (a->hash < b->hash)
|
||||
return -1;
|
||||
|
||||
if (a->hash > b->hash)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PICO_TREE_DECLARE(Datalink_proto_tree, pico_proto_cmp);
|
||||
PICO_TREE_DECLARE(Network_proto_tree, pico_proto_cmp);
|
||||
PICO_TREE_DECLARE(Transport_proto_tree, pico_proto_cmp);
|
||||
PICO_TREE_DECLARE(Socket_proto_tree, pico_proto_cmp);
|
||||
|
||||
/* Static variables to keep track of the round robin loop */
|
||||
static struct pico_proto_rr proto_rr_datalink = {
|
||||
&Datalink_proto_tree, NULL, NULL
|
||||
};
|
||||
static struct pico_proto_rr proto_rr_network = {
|
||||
&Network_proto_tree, NULL, NULL
|
||||
};
|
||||
static struct pico_proto_rr proto_rr_transport = {
|
||||
&Transport_proto_tree, NULL, NULL
|
||||
};
|
||||
static struct pico_proto_rr proto_rr_socket = {
|
||||
&Socket_proto_tree, NULL, NULL
|
||||
};
|
||||
|
||||
static int proto_loop_in(struct pico_protocol *proto, int loop_score)
|
||||
{
|
||||
struct pico_frame *f;
|
||||
while(loop_score > 0) {
|
||||
if (proto->q_in->frames == 0)
|
||||
break;
|
||||
|
||||
f = pico_dequeue(proto->q_in);
|
||||
if ((f) && (proto->process_in(proto, f) > 0)) {
|
||||
loop_score--;
|
||||
}
|
||||
}
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static int proto_loop_out(struct pico_protocol *proto, int loop_score)
|
||||
{
|
||||
struct pico_frame *f;
|
||||
while(loop_score > 0) {
|
||||
if (proto->q_out->frames == 0)
|
||||
break;
|
||||
|
||||
f = pico_dequeue(proto->q_out);
|
||||
if ((f) && (proto->process_out(proto, f) > 0)) {
|
||||
loop_score--;
|
||||
}
|
||||
}
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static int proto_loop(struct pico_protocol *proto, int loop_score, int direction)
|
||||
{
|
||||
|
||||
if (direction == PICO_LOOP_DIR_IN)
|
||||
loop_score = proto_loop_in(proto, loop_score);
|
||||
else if (direction == PICO_LOOP_DIR_OUT)
|
||||
loop_score = proto_loop_out(proto, loop_score);
|
||||
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static struct pico_tree_node *roundrobin_init(struct pico_proto_rr *rr, int direction)
|
||||
{
|
||||
struct pico_tree_node *next_node = NULL;
|
||||
/* Initialization (takes place only once) */
|
||||
if (rr->node_in == NULL)
|
||||
rr->node_in = pico_tree_firstNode(rr->t->root);
|
||||
|
||||
if (rr->node_out == NULL)
|
||||
rr->node_out = pico_tree_firstNode(rr->t->root);
|
||||
|
||||
if (direction == PICO_LOOP_DIR_IN)
|
||||
next_node = rr->node_in;
|
||||
else
|
||||
next_node = rr->node_out;
|
||||
|
||||
return next_node;
|
||||
}
|
||||
|
||||
static void roundrobin_end(struct pico_proto_rr *rr, int direction, struct pico_tree_node *last)
|
||||
{
|
||||
if (direction == PICO_LOOP_DIR_IN)
|
||||
rr->node_in = last;
|
||||
else
|
||||
rr->node_out = last;
|
||||
}
|
||||
|
||||
static int pico_protocol_generic_loop(struct pico_proto_rr *rr, int loop_score, int direction)
|
||||
{
|
||||
struct pico_protocol *start, *next;
|
||||
struct pico_tree_node *next_node = roundrobin_init(rr, direction);
|
||||
|
||||
if (!next_node)
|
||||
return loop_score;
|
||||
|
||||
next = next_node->keyValue;
|
||||
|
||||
/* init start node */
|
||||
start = next;
|
||||
|
||||
/* round-robin all layer protocols, break if traversed all protocols */
|
||||
while (loop_score > 1 && next != NULL) {
|
||||
loop_score = proto_loop(next, loop_score, direction);
|
||||
next_node = pico_tree_next(next_node);
|
||||
next = next_node->keyValue;
|
||||
if (next == NULL)
|
||||
{
|
||||
next_node = pico_tree_firstNode(rr->t->root);
|
||||
next = next_node->keyValue;
|
||||
}
|
||||
|
||||
if (next == start)
|
||||
break;
|
||||
}
|
||||
roundrobin_end(rr, direction, next_node);
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
int pico_protocol_datalink_loop(int loop_score, int direction)
|
||||
{
|
||||
return pico_protocol_generic_loop(&proto_rr_datalink, loop_score, direction);
|
||||
}
|
||||
|
||||
int pico_protocol_network_loop(int loop_score, int direction)
|
||||
{
|
||||
return pico_protocol_generic_loop(&proto_rr_network, loop_score, direction);
|
||||
}
|
||||
|
||||
int pico_protocol_transport_loop(int loop_score, int direction)
|
||||
{
|
||||
return pico_protocol_generic_loop(&proto_rr_transport, loop_score, direction);
|
||||
}
|
||||
|
||||
int pico_protocol_socket_loop(int loop_score, int direction)
|
||||
{
|
||||
return pico_protocol_generic_loop(&proto_rr_socket, loop_score, direction);
|
||||
}
|
||||
|
||||
int pico_protocols_loop(int loop_score)
|
||||
{
|
||||
/*
|
||||
loop_score = pico_protocol_datalink_loop(loop_score);
|
||||
loop_score = pico_protocol_network_loop(loop_score);
|
||||
loop_score = pico_protocol_transport_loop(loop_score);
|
||||
loop_score = pico_protocol_socket_loop(loop_score);
|
||||
*/
|
||||
return loop_score;
|
||||
}
|
||||
|
||||
static void proto_layer_rr_reset(struct pico_proto_rr *rr)
|
||||
{
|
||||
rr->node_in = NULL;
|
||||
rr->node_out = NULL;
|
||||
}
|
||||
|
||||
void pico_protocol_init(struct pico_protocol *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p->hash = pico_hash(p->name, (uint32_t)strlen(p->name));
|
||||
switch (p->layer) {
|
||||
case PICO_LAYER_DATALINK:
|
||||
pico_tree_insert(&Datalink_proto_tree, p);
|
||||
proto_layer_rr_reset(&proto_rr_datalink);
|
||||
break;
|
||||
case PICO_LAYER_NETWORK:
|
||||
pico_tree_insert(&Network_proto_tree, p);
|
||||
proto_layer_rr_reset(&proto_rr_network);
|
||||
break;
|
||||
case PICO_LAYER_TRANSPORT:
|
||||
pico_tree_insert(&Transport_proto_tree, p);
|
||||
proto_layer_rr_reset(&proto_rr_transport);
|
||||
break;
|
||||
case PICO_LAYER_SOCKET:
|
||||
pico_tree_insert(&Socket_proto_tree, p);
|
||||
proto_layer_rr_reset(&proto_rr_socket);
|
||||
break;
|
||||
}
|
||||
//dbg("Protocol %s registered (layer: %d).\n", p->name, p->layer);
|
||||
|
||||
}
|
||||
|
||||
2231
ext/picotcp/stack/pico_socket.c
Normal file
2231
ext/picotcp/stack/pico_socket.c
Normal file
File diff suppressed because it is too large
Load Diff
1340
ext/picotcp/stack/pico_socket_multicast.c
Normal file
1340
ext/picotcp/stack/pico_socket_multicast.c
Normal file
File diff suppressed because it is too large
Load Diff
1165
ext/picotcp/stack/pico_stack.c
Normal file
1165
ext/picotcp/stack/pico_stack.c
Normal file
File diff suppressed because it is too large
Load Diff
575
ext/picotcp/stack/pico_tree.c
Normal file
575
ext/picotcp/stack/pico_tree.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Author: Andrei Carp <andrei.carp@tass.be>
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_tree.h"
|
||||
#include "pico_config.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_mm.h"
|
||||
|
||||
#define RED 0
|
||||
#define BLACK 1
|
||||
|
||||
/* By default the null leafs are black */
|
||||
struct pico_tree_node LEAF = {
|
||||
NULL, /* key */
|
||||
&LEAF, &LEAF, &LEAF, /* parent, left,right */
|
||||
BLACK, /* color */
|
||||
};
|
||||
|
||||
#define IS_LEAF(x) (x == &LEAF)
|
||||
#define IS_NOT_LEAF(x) (x != &LEAF)
|
||||
#define INIT_LEAF (&LEAF)
|
||||
|
||||
#define AM_I_LEFT_CHILD(x) (x == x->parent->leftChild)
|
||||
#define AM_I_RIGHT_CHILD(x) (x == x->parent->rightChild)
|
||||
|
||||
#define PARENT(x) (x->parent)
|
||||
#define GRANPA(x) (x->parent->parent)
|
||||
|
||||
/*
|
||||
* Local Functions
|
||||
*/
|
||||
static struct pico_tree_node *create_node(struct pico_tree *tree, void *key, uint8_t allocator);
|
||||
static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node);
|
||||
static void rotateToRight(struct pico_tree*root, struct pico_tree_node*node);
|
||||
static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node);
|
||||
static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node);
|
||||
static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB);
|
||||
void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
|
||||
void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
|
||||
|
||||
#ifdef PICO_SUPPORT_MM
|
||||
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
|
||||
* These nodes should be placed in the manager page which is in a different memory region then the nodes
|
||||
* which are used for the pico stack in general.
|
||||
* Therefore the following 2 functions are created so that pico_tree can use them to to put these nodes
|
||||
* into the correct memory regions.
|
||||
* If pico_tree_insert is called from the memory manager module, then create_node should use
|
||||
* pico_mem_page0_zalloc to create a node. The same for pico_tree_delete.
|
||||
*/
|
||||
extern void*pico_mem_page0_zalloc(size_t len);
|
||||
extern void pico_mem_page0_free(void*ptr);
|
||||
#endif /* PICO_SUPPORT_MM */
|
||||
|
||||
/*
|
||||
* Exported functions
|
||||
*/
|
||||
|
||||
struct pico_tree_node *pico_tree_firstNode(struct pico_tree_node *node)
|
||||
{
|
||||
while(IS_NOT_LEAF(node->leftChild))
|
||||
node = node->leftChild;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct pico_tree_node *pico_tree_lastNode(struct pico_tree_node *node)
|
||||
{
|
||||
while(IS_NOT_LEAF(node->rightChild))
|
||||
node = node->rightChild;
|
||||
return node;
|
||||
}
|
||||
|
||||
struct pico_tree_node *pico_tree_next(struct pico_tree_node *node)
|
||||
{
|
||||
if (!node)
|
||||
return NULL;
|
||||
if(IS_NOT_LEAF(node->rightChild))
|
||||
{
|
||||
node = node->rightChild;
|
||||
while(IS_NOT_LEAF(node->leftChild))
|
||||
node = node->leftChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IS_NOT_LEAF(node->parent) && AM_I_LEFT_CHILD(node))
|
||||
node = node->parent;
|
||||
else {
|
||||
while (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node))
|
||||
node = node->parent;
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct pico_tree_node *pico_tree_prev(struct pico_tree_node *node)
|
||||
{
|
||||
if (IS_NOT_LEAF(node->leftChild)) {
|
||||
node = node->leftChild;
|
||||
while (IS_NOT_LEAF(node->rightChild))
|
||||
node = node->rightChild;
|
||||
} else {
|
||||
if (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node))
|
||||
node = node->parent;
|
||||
else {
|
||||
while (IS_NOT_LEAF(node) && AM_I_LEFT_CHILD(node))
|
||||
node = node->parent;
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
|
||||
* These nodes should be placed in the manager page which is in a different memory region then the nodes
|
||||
* which are used for the pico stack in general.
|
||||
* Therefore the following wrapper for pico_tree_insert is created.
|
||||
* The actual implementation can be found in pico_tree_insert_implementation.
|
||||
*/
|
||||
void *pico_tree_insert(struct pico_tree *tree, void *key)
|
||||
{
|
||||
return pico_tree_insert_implementation(tree, key, USE_PICO_ZALLOC);
|
||||
}
|
||||
|
||||
void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
|
||||
{
|
||||
struct pico_tree_node *last_node = INIT_LEAF;
|
||||
struct pico_tree_node *temp = tree->root;
|
||||
struct pico_tree_node *insert;
|
||||
void *LocalKey;
|
||||
int result = 0;
|
||||
|
||||
LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL);
|
||||
|
||||
/* if node already in, bail out */
|
||||
if(LocalKey) {
|
||||
return LocalKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(allocator == USE_PICO_PAGE0_ZALLOC)
|
||||
insert = create_node(tree, key, USE_PICO_PAGE0_ZALLOC);
|
||||
else
|
||||
insert = create_node(tree, key, USE_PICO_ZALLOC);
|
||||
|
||||
if(!insert)
|
||||
{
|
||||
pico_err = PICO_ERR_ENOMEM;
|
||||
/* to let the user know that it couldn't insert */
|
||||
return (void *)&LEAF;
|
||||
}
|
||||
}
|
||||
|
||||
/* search for the place to insert the new node */
|
||||
while(IS_NOT_LEAF(temp))
|
||||
{
|
||||
last_node = temp;
|
||||
result = tree->compare(insert->keyValue, temp->keyValue);
|
||||
|
||||
temp = (result < 0) ? (temp->leftChild) : (temp->rightChild);
|
||||
}
|
||||
/* make the needed connections */
|
||||
insert->parent = last_node;
|
||||
|
||||
if(IS_LEAF(last_node))
|
||||
tree->root = insert;
|
||||
else{
|
||||
result = tree->compare(insert->keyValue, last_node->keyValue);
|
||||
if(result < 0)
|
||||
last_node->leftChild = insert;
|
||||
else
|
||||
last_node->rightChild = insert;
|
||||
}
|
||||
|
||||
/* fix colour issues */
|
||||
fix_insert_collisions(tree, insert);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key)
|
||||
{
|
||||
struct pico_tree_node *found;
|
||||
|
||||
found = tree->root;
|
||||
|
||||
while(IS_NOT_LEAF(found))
|
||||
{
|
||||
int result;
|
||||
result = tree->compare(found->keyValue, key);
|
||||
if(result == 0)
|
||||
{
|
||||
return found;
|
||||
}
|
||||
else if(result < 0)
|
||||
found = found->rightChild;
|
||||
else
|
||||
found = found->leftChild;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *pico_tree_findKey(struct pico_tree *tree, void *key)
|
||||
{
|
||||
struct pico_tree_node *found;
|
||||
|
||||
|
||||
found = tree->root;
|
||||
while(IS_NOT_LEAF(found))
|
||||
{
|
||||
int result;
|
||||
|
||||
result = tree->compare(found->keyValue, key);
|
||||
if(result == 0)
|
||||
return found->keyValue;
|
||||
else if(result < 0)
|
||||
found = found->rightChild;
|
||||
else
|
||||
found = found->leftChild;
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *pico_tree_first(struct pico_tree *tree)
|
||||
{
|
||||
return pico_tree_firstNode(tree->root)->keyValue;
|
||||
}
|
||||
|
||||
void *pico_tree_last(struct pico_tree *tree)
|
||||
{
|
||||
return pico_tree_lastNode(tree->root)->keyValue;
|
||||
}
|
||||
|
||||
static uint8_t pico_tree_delete_node(struct pico_tree *tree, struct pico_tree_node *d, struct pico_tree_node **temp)
|
||||
{
|
||||
struct pico_tree_node *min;
|
||||
struct pico_tree_node *ltemp = d;
|
||||
uint8_t nodeColor;
|
||||
min = pico_tree_firstNode(d->rightChild);
|
||||
nodeColor = min->color;
|
||||
|
||||
*temp = min->rightChild;
|
||||
if(min->parent == ltemp && IS_NOT_LEAF(*temp))
|
||||
(*temp)->parent = min;
|
||||
else{
|
||||
switchNodes(tree, min, min->rightChild);
|
||||
min->rightChild = ltemp->rightChild;
|
||||
if(IS_NOT_LEAF(min->rightChild)) min->rightChild->parent = min;
|
||||
}
|
||||
|
||||
switchNodes(tree, ltemp, min);
|
||||
min->leftChild = ltemp->leftChild;
|
||||
|
||||
if(IS_NOT_LEAF(min->leftChild))
|
||||
min->leftChild->parent = min;
|
||||
|
||||
min->color = ltemp->color;
|
||||
return nodeColor;
|
||||
}
|
||||
|
||||
static uint8_t pico_tree_delete_check_switch(struct pico_tree *tree, struct pico_tree_node *delete, struct pico_tree_node **temp)
|
||||
{
|
||||
struct pico_tree_node *ltemp = delete;
|
||||
uint8_t nodeColor = delete->color;
|
||||
if(IS_LEAF(delete->leftChild))
|
||||
{
|
||||
*temp = ltemp->rightChild;
|
||||
switchNodes(tree, ltemp, ltemp->rightChild);
|
||||
}
|
||||
else
|
||||
if(IS_LEAF(delete->rightChild))
|
||||
{
|
||||
struct pico_tree_node *_ltemp = delete;
|
||||
*temp = _ltemp->leftChild;
|
||||
switchNodes(tree, _ltemp, _ltemp->leftChild);
|
||||
}
|
||||
else{
|
||||
nodeColor = pico_tree_delete_node(tree, delete, temp);
|
||||
}
|
||||
|
||||
return nodeColor;
|
||||
|
||||
}
|
||||
|
||||
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
|
||||
* These nodes should be placed in the manager page which is in a different memory region then the nodes
|
||||
* which are used for the pico stack in general.
|
||||
* Therefore the following wrapper for pico_tree_delete is created.
|
||||
* The actual implementation can be found in pico_tree_delete_implementation.
|
||||
*/
|
||||
void *pico_tree_delete(struct pico_tree *tree, void *key)
|
||||
{
|
||||
return pico_tree_delete_implementation(tree, key, USE_PICO_ZALLOC);
|
||||
}
|
||||
|
||||
static inline void if_nodecolor_black_fix_collisions(struct pico_tree *tree, struct pico_tree_node *temp, uint8_t nodeColor)
|
||||
{
|
||||
/* deleted node is black, this will mess up the black path property */
|
||||
if(nodeColor == BLACK)
|
||||
fix_delete_collisions(tree, temp);
|
||||
}
|
||||
|
||||
void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
|
||||
{
|
||||
struct pico_tree_node *temp;
|
||||
uint8_t nodeColor; /* keeps the color of the node to be deleted */
|
||||
void *lkey; /* keeps a copy of the key which will be removed */
|
||||
struct pico_tree_node *delete; /* keeps a copy of the node to be extracted */
|
||||
if (!key)
|
||||
return NULL;
|
||||
delete = pico_tree_findNode(tree, key);
|
||||
|
||||
/* this key isn't in the tree, bail out */
|
||||
if(!delete)
|
||||
return NULL;
|
||||
|
||||
lkey = delete->keyValue;
|
||||
nodeColor = pico_tree_delete_check_switch(tree, delete, &temp);
|
||||
|
||||
if_nodecolor_black_fix_collisions(tree, temp, nodeColor);
|
||||
|
||||
if(allocator == USE_PICO_ZALLOC)
|
||||
PICO_FREE(delete);
|
||||
|
||||
#ifdef PICO_SUPPORT_MM
|
||||
else
|
||||
pico_mem_page0_free(delete);
|
||||
#endif
|
||||
return lkey;
|
||||
}
|
||||
|
||||
int pico_tree_empty(struct pico_tree *tree)
|
||||
{
|
||||
return (!tree->root || IS_LEAF(tree->root));
|
||||
}
|
||||
|
||||
/*
|
||||
* Private functions
|
||||
*/
|
||||
static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node)
|
||||
{
|
||||
struct pico_tree_node*temp;
|
||||
|
||||
temp = node->rightChild;
|
||||
|
||||
if(temp == &LEAF) return;
|
||||
|
||||
node->rightChild = temp->leftChild;
|
||||
|
||||
if(IS_NOT_LEAF(temp->leftChild))
|
||||
temp->leftChild->parent = node;
|
||||
|
||||
temp->parent = node->parent;
|
||||
|
||||
if(IS_LEAF(node->parent))
|
||||
tree->root = temp;
|
||||
else
|
||||
if(node == node->parent->leftChild)
|
||||
node->parent->leftChild = temp;
|
||||
else
|
||||
node->parent->rightChild = temp;
|
||||
|
||||
temp->leftChild = node;
|
||||
node->parent = temp;
|
||||
}
|
||||
|
||||
|
||||
static void rotateToRight(struct pico_tree *tree, struct pico_tree_node *node)
|
||||
{
|
||||
struct pico_tree_node*temp;
|
||||
|
||||
temp = node->leftChild;
|
||||
node->leftChild = temp->rightChild;
|
||||
|
||||
if(temp == &LEAF) return;
|
||||
|
||||
if(IS_NOT_LEAF(temp->rightChild))
|
||||
temp->rightChild->parent = node;
|
||||
|
||||
temp->parent = node->parent;
|
||||
|
||||
if(IS_LEAF(node->parent))
|
||||
tree->root = temp;
|
||||
else
|
||||
if(node == node->parent->rightChild)
|
||||
node->parent->rightChild = temp;
|
||||
else
|
||||
node->parent->leftChild = temp;
|
||||
|
||||
temp->rightChild = node;
|
||||
node->parent = temp;
|
||||
return;
|
||||
}
|
||||
|
||||
static struct pico_tree_node *create_node(struct pico_tree *tree, void*key, uint8_t allocator)
|
||||
{
|
||||
struct pico_tree_node *temp = NULL;
|
||||
IGNORE_PARAMETER(tree);
|
||||
if(allocator == USE_PICO_ZALLOC)
|
||||
temp = (struct pico_tree_node *)PICO_ZALLOC(sizeof(struct pico_tree_node));
|
||||
|
||||
#ifdef PICO_SUPPORT_MM
|
||||
else
|
||||
temp = (struct pico_tree_node *)pico_mem_page0_zalloc(sizeof(struct pico_tree_node));
|
||||
#endif
|
||||
|
||||
if(!temp)
|
||||
return NULL;
|
||||
|
||||
temp->keyValue = key;
|
||||
temp->parent = &LEAF;
|
||||
temp->leftChild = &LEAF;
|
||||
temp->rightChild = &LEAF;
|
||||
/* by default every new node is red */
|
||||
temp->color = RED;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function fixes the possible collisions in the tree.
|
||||
* Eg. if a node is red his children must be black !
|
||||
*/
|
||||
static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node)
|
||||
{
|
||||
struct pico_tree_node*temp;
|
||||
|
||||
while(node->parent->color == RED && IS_NOT_LEAF(GRANPA(node)))
|
||||
{
|
||||
if(AM_I_RIGHT_CHILD(node->parent))
|
||||
{
|
||||
temp = GRANPA(node)->leftChild;
|
||||
if(temp->color == RED) {
|
||||
node->parent->color = BLACK;
|
||||
temp->color = BLACK;
|
||||
GRANPA(node)->color = RED;
|
||||
node = GRANPA(node);
|
||||
}
|
||||
else if(temp->color == BLACK) {
|
||||
if(node == node->parent->leftChild) {
|
||||
node = node->parent;
|
||||
rotateToRight(tree, node);
|
||||
}
|
||||
|
||||
node->parent->color = BLACK;
|
||||
GRANPA(node)->color = RED;
|
||||
rotateToLeft(tree, GRANPA(node));
|
||||
}
|
||||
}
|
||||
else if(AM_I_LEFT_CHILD(node->parent))
|
||||
{
|
||||
temp = GRANPA(node)->rightChild;
|
||||
if(temp->color == RED) {
|
||||
node->parent->color = BLACK;
|
||||
temp->color = BLACK;
|
||||
GRANPA(node)->color = RED;
|
||||
node = GRANPA(node);
|
||||
}
|
||||
else if(temp->color == BLACK) {
|
||||
if(AM_I_RIGHT_CHILD(node)) {
|
||||
node = node->parent;
|
||||
rotateToLeft(tree, node);
|
||||
}
|
||||
|
||||
node->parent->color = BLACK;
|
||||
GRANPA(node)->color = RED;
|
||||
rotateToRight(tree, GRANPA(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* make sure that the root of the tree stays black */
|
||||
tree->root->color = BLACK;
|
||||
}
|
||||
|
||||
static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB)
|
||||
{
|
||||
|
||||
if(IS_LEAF(nodeA->parent))
|
||||
tree->root = nodeB;
|
||||
else
|
||||
if(IS_NOT_LEAF(nodeA))
|
||||
{
|
||||
if(AM_I_LEFT_CHILD(nodeA))
|
||||
nodeA->parent->leftChild = nodeB;
|
||||
else
|
||||
nodeA->parent->rightChild = nodeB;
|
||||
}
|
||||
|
||||
if(IS_NOT_LEAF(nodeB)) nodeB->parent = nodeA->parent;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This function fixes the possible collisions in the tree.
|
||||
* Eg. if a node is red his children must be black !
|
||||
* In this case the function fixes the constant black path property.
|
||||
*/
|
||||
static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node)
|
||||
{
|
||||
struct pico_tree_node*temp;
|
||||
|
||||
while( node != tree->root && node->color == BLACK && IS_NOT_LEAF(node))
|
||||
{
|
||||
if(AM_I_LEFT_CHILD(node)) {
|
||||
|
||||
temp = node->parent->rightChild;
|
||||
if(temp->color == RED)
|
||||
{
|
||||
temp->color = BLACK;
|
||||
node->parent->color = RED;
|
||||
rotateToLeft(tree, node->parent);
|
||||
temp = node->parent->rightChild;
|
||||
}
|
||||
|
||||
if(temp->leftChild->color == BLACK && temp->rightChild->color == BLACK)
|
||||
{
|
||||
temp->color = RED;
|
||||
node = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(temp->rightChild->color == BLACK)
|
||||
{
|
||||
temp->leftChild->color = BLACK;
|
||||
temp->color = RED;
|
||||
rotateToRight(tree, temp);
|
||||
temp = temp->parent->rightChild;
|
||||
}
|
||||
|
||||
temp->color = node->parent->color;
|
||||
node->parent->color = BLACK;
|
||||
temp->rightChild->color = BLACK;
|
||||
rotateToLeft(tree, node->parent);
|
||||
node = tree->root;
|
||||
}
|
||||
}
|
||||
else{
|
||||
temp = node->parent->leftChild;
|
||||
if(temp->color == RED)
|
||||
{
|
||||
temp->color = BLACK;
|
||||
node->parent->color = RED;
|
||||
rotateToRight(tree, node->parent);
|
||||
temp = node->parent->leftChild;
|
||||
}
|
||||
|
||||
if(temp->rightChild->color == BLACK && temp->leftChild->color == BLACK)
|
||||
{
|
||||
temp->color = RED;
|
||||
node = node->parent;
|
||||
}
|
||||
else{
|
||||
if(temp->leftChild->color == BLACK)
|
||||
{
|
||||
temp->rightChild->color = BLACK;
|
||||
temp->color = RED;
|
||||
rotateToLeft(tree, temp);
|
||||
temp = temp->parent->leftChild;
|
||||
}
|
||||
|
||||
temp->color = node->parent->color;
|
||||
node->parent->color = BLACK;
|
||||
temp->leftChild->color = BLACK;
|
||||
rotateToRight(tree, node->parent);
|
||||
node = tree->root;
|
||||
}
|
||||
}
|
||||
}
|
||||
node->color = BLACK;
|
||||
}
|
||||
Reference in New Issue
Block a user