Add a module to manage a list of connections
This commit is contained in:
parent
03912a6185
commit
c4e6028d42
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