Add a module to manage a list of connections
This commit is contained in:
parent
a0623668a7
commit
ec3244a61f
142
impl/src/layer2/connection_list.c
Normal file
142
impl/src/layer2/connection_list.c
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "connection_list.h"
|
||||||
|
|
||||||
|
#include "results.h"
|
||||||
|
|
||||||
|
#define SEQ_NR_MASK 0xF
|
||||||
|
|
||||||
|
// find the location where the timestamp should be inserted to maintain
|
||||||
|
// ascending timestamp order and return the then-previous entry.
|
||||||
|
//
|
||||||
|
// If NULL is returned, the list is either empty or the entry should be
|
||||||
|
// inserted before the head. This must be checked externally.
|
||||||
|
static connection_list_entry_t* find_prev_for_timestamp(connection_list_t *list, uint64_t timestamp)
|
||||||
|
{
|
||||||
|
if(!list->head) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timestamp < list->head->next_access_time) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection_list_entry_t *prev = list->head;
|
||||||
|
while(prev->next) {
|
||||||
|
if(prev->next_access_time < timestamp
|
||||||
|
&& prev->next->next_access_time >= timestamp) {
|
||||||
|
// location found!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = prev->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// either the correct location was found or prev now points to the last entry
|
||||||
|
// in the list.
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_t connection_list_init(connection_list_t *list)
|
||||||
|
{
|
||||||
|
list->head = NULL;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void connection_list_destroy(connection_list_t *list)
|
||||||
|
{
|
||||||
|
// delete all list entries
|
||||||
|
while(list->head) {
|
||||||
|
connection_list_entry_t *next = list->head->next;
|
||||||
|
free(list->head);
|
||||||
|
list->head = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t connection_list_insert(connection_list_t *list, const connection_ctx_t *conn, uint64_t next_access_time)
|
||||||
|
{
|
||||||
|
connection_list_entry_t *new_entry = malloc(sizeof(connection_list_entry_t));
|
||||||
|
if(!new_entry) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_entry->connection = *conn;
|
||||||
|
new_entry->next_access_time = next_access_time;
|
||||||
|
|
||||||
|
connection_list_entry_t *prev = find_prev_for_timestamp(list, next_access_time);
|
||||||
|
|
||||||
|
if(!prev) {
|
||||||
|
// the new entry should become the list’s head (also works if the list is empty!)
|
||||||
|
new_entry->next = list->head;
|
||||||
|
list->head = new_entry;
|
||||||
|
} else {
|
||||||
|
// insert after prev
|
||||||
|
new_entry->next = prev->next;
|
||||||
|
prev->next = new_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
connection_list_entry_t* connection_list_get_head(connection_list_t *list)
|
||||||
|
{
|
||||||
|
return list->head;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t connection_list_reschedule_head(connection_list_t *list, uint64_t next_access_timestamp)
|
||||||
|
{
|
||||||
|
if(!list->head) {
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!list->head->next) {
|
||||||
|
// nothing to do because there is only one entry.
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(next_access_timestamp < list->head->next->next_access_time) {
|
||||||
|
// the head does not need to be moved because the new timestamp is still
|
||||||
|
// smaller than that of the second entry => only update the timestamp.
|
||||||
|
list->head->next_access_time = next_access_timestamp;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// detach the entry from the list
|
||||||
|
connection_list_entry_t *reloc_entry = list->head;
|
||||||
|
list->head = list->head->next;
|
||||||
|
|
||||||
|
// find the location where the entry should be reinserted
|
||||||
|
connection_list_entry_t *prev = find_prev_for_timestamp(list, next_access_timestamp);
|
||||||
|
|
||||||
|
if(!prev) {
|
||||||
|
// the relocated entry should become the list’s head (also works if the list is empty!)
|
||||||
|
reloc_entry->next = list->head;
|
||||||
|
list->head = reloc_entry;
|
||||||
|
} else {
|
||||||
|
// insert after prev
|
||||||
|
reloc_entry->next = prev->next;
|
||||||
|
prev->next = reloc_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t connection_list_delete_head(connection_list_t *list)
|
||||||
|
{
|
||||||
|
if(!list->head) {
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection_list_entry_t *new_head = list->head->next;
|
||||||
|
|
||||||
|
free(list->head);
|
||||||
|
list->head = new_head;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
78
impl/src/layer2/connection_list.h
Normal file
78
impl/src/layer2/connection_list.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* This file contains functions to manage a list of layer 2 connections for
|
||||||
|
* scheduling purposes.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Thomas Kolb
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTION_LIST_H
|
||||||
|
#define CONNECTION_LIST_H
|
||||||
|
|
||||||
|
#include <results.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
typedef struct connection_list_entry_s {
|
||||||
|
uint64_t next_access_time; //!< When to next activate this connection
|
||||||
|
connection_ctx_t connection; //!< The actual connection entry
|
||||||
|
|
||||||
|
struct connection_list_entry_s *next; //!< pointer to the next list element
|
||||||
|
} connection_list_entry_t;
|
||||||
|
|
||||||
|
typedef struct connection_list_s {
|
||||||
|
connection_list_entry_t *head; //!< pointer to the first list element
|
||||||
|
} connection_list_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*!\brief Initialize a connection list.
|
||||||
|
*
|
||||||
|
* \param list The list to initialize.
|
||||||
|
* \returns OK if everything worked or a fitting error code.
|
||||||
|
*/
|
||||||
|
result_t connection_list_init(connection_list_t *list);
|
||||||
|
|
||||||
|
/*!\brief Destroy the given layer 2 connection list, deleting all internal data.
|
||||||
|
*/
|
||||||
|
void connection_list_destroy(connection_list_t *list);
|
||||||
|
|
||||||
|
/*!\brief Insert a connection context into the list.
|
||||||
|
*
|
||||||
|
* The connection context will be copied internally. The original copy should
|
||||||
|
* no longer be used after the context was inserted into the list.
|
||||||
|
*
|
||||||
|
* \param list Pointer to the list where the entry shall be inserted.
|
||||||
|
* \param conn Pointer to the connection context.
|
||||||
|
* \param next_access_time Timestamp when this connection should be activated.
|
||||||
|
* \returns OK if everything worked or a fitting error code.
|
||||||
|
*/
|
||||||
|
result_t connection_list_insert(connection_list_t *list, const connection_ctx_t *conn, uint64_t next_access_time);
|
||||||
|
|
||||||
|
/*!\brief Get the head (first entry) of the list.
|
||||||
|
*
|
||||||
|
* \param list Pointer to the list to access.
|
||||||
|
*
|
||||||
|
* \returns A pointer to the head entry or NULL if the list is empty.
|
||||||
|
*/
|
||||||
|
connection_list_entry_t* connection_list_get_head(connection_list_t *list);
|
||||||
|
|
||||||
|
/*!\brief Re-schedule the current head entry.
|
||||||
|
*
|
||||||
|
* \param list Pointer to the list to access.
|
||||||
|
* \param next_access_time Timestamp when this connection should be activated again.
|
||||||
|
*
|
||||||
|
* \returns OK if everything worked or a fitting error code.
|
||||||
|
*/
|
||||||
|
result_t connection_list_reschedule_head(connection_list_t *list, uint64_t next_access_timestamp);
|
||||||
|
|
||||||
|
/*!\brief Delete the current head entry.
|
||||||
|
*
|
||||||
|
* \param list Pointer to the list to access.
|
||||||
|
*
|
||||||
|
* \returns OK if everything worked or a fitting error code.
|
||||||
|
*/
|
||||||
|
result_t connection_list_delete_head(connection_list_t *list);
|
||||||
|
|
||||||
|
#endif // CONNECTION_LIST_H
|
Loading…
Reference in a new issue