Skip to content

File baton_queue.c

File List > src > baton_queue.c

Go to the documentation of this file

/*******************************************************************************
 * Baton-C: Deterministic Master/Slave Messaging over a Single-Wire UART
 *
 * Project:     Baton-C - Deterministic Master/Slave Messaging over a Single-Wire UART
 * Description: Event queue: ring buffer of variable-size records
 *
 * Author:      Anton Vynohradov
 * Email:       avynohradov@systemfromscratch.com
 *
 * License:     Apache License 2.0
 *
 * Copyright (c) 2026 Anton Vynohradov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 ******************************************************************************/

/* ==============================================================================
 * INCLUDES
 * =============================================================================*/

#include <stddef.h>
#include <string.h>

#include "baton_defs.h"
#include "baton_queue.h"

/* ==============================================================================
 * DEFINES & MACROS
 * =============================================================================*/

#define REC_PENDING 0U
#define REC_INFLIGHT 4U
#define REC_STAMP 8U
#define REC_MSG_ID 12U
#define REC_LENGTH 13U
#define REC_DST 14U
#define REC_FLAGS 15U

/* ==============================================================================
 * PRIVATE FUNCTION DECLARATIONS (static)
 * =============================================================================*/

static uint16_t align4(uint16_t value);
static uint16_t normalize(const struct baton_queue* queue, uint16_t offset, uint16_t tail);

/* ==============================================================================
 * PRIVATE FUNCTION IMPLEMENTATIONS
 * =============================================================================*/

static uint16_t align4(uint16_t value)
{
    return (uint16_t) ((uint16_t) (value + 3U) & (uint16_t) 0xFFFCU);
}

static uint16_t normalize(const struct baton_queue* queue, uint16_t offset, uint16_t tail)
{
    uint16_t result = offset;

    if (offset != tail)
    {
        if (((uint32_t) offset + BATON_REC_HEADER_SIZE) > (uint32_t) queue->size)
        {
            result = 0U;
        }
        else if ((queue->memory[offset + REC_FLAGS] & BATON_REC_SKIP) != 0U)
        {
            result = 0U;
        }
        else
        {
            /* a record starts here */
        }
    }

    return result;
}

/* ==============================================================================
 * PUBLIC FUNCTION IMPLEMENTATIONS
 * =============================================================================*/

void baton_queue_init(struct baton_queue* queue, uint8_t* memory, uint16_t size)
{
    if ((queue != NULL) && (memory != NULL))
    {
        queue->memory = memory;
        queue->size = (uint16_t) (size & (uint16_t) 0xFFFCU);
        queue->head = 0U;
        queue->tail = 0U;
    }
}

uint16_t baton_queue_record_size(uint8_t length)
{
    return (uint16_t) (BATON_REC_HEADER_SIZE + align4((uint16_t) length));
}

bool baton_queue_alloc(struct baton_queue* queue, uint16_t need, uint16_t* offset)
{
    bool reserved = false;

    if ((queue != NULL) && (offset != NULL) && (need >= BATON_REC_HEADER_SIZE))
    {
        uint16_t head;
        uint16_t tail;

        if (queue->head == queue->tail)
        {
            queue->head = 0U;
            queue->tail = 0U;
        }
        head = queue->head;
        tail = queue->tail;

        if (tail >= head)
        {
            if (((uint32_t) queue->size - (uint32_t) tail) >= (uint32_t) need)
            {
                *offset = tail;
                queue->tail = (uint16_t) (tail + need);
                reserved = true;
            }
            else if (head > need) /* wrap; strict so that tail never catches head */
            {
                if (((uint32_t) queue->size - (uint32_t) tail) >= BATON_REC_HEADER_SIZE)
                {
                    (void) memset(&queue->memory[tail], 0, BATON_REC_HEADER_SIZE);
                    queue->memory[tail + REC_FLAGS] = BATON_REC_SKIP;
                }
                *offset = 0U;
                queue->tail = need;
                reserved = true;
            }
            else
            {
                /* no room at the end and not enough before the head */
            }
        }
        else if ((uint16_t) (head - tail) > need)
        {
            *offset = tail;
            queue->tail = (uint16_t) (tail + need);
            reserved = true;
        }
        else
        {
            /* no room between tail and head */
        }
    }

    return reserved;
}

uint16_t baton_queue_first(const struct baton_queue* queue, uint16_t head, uint16_t tail)
{
    uint16_t result = tail;

    if (queue != NULL)
    {
        result = normalize(queue, head, tail);
    }

    return result;
}

uint16_t baton_queue_next(const struct baton_queue* queue, uint16_t offset, uint16_t tail)
{
    uint16_t result = tail;

    if (queue != NULL)
    {
        const uint8_t length = queue->memory[offset + REC_LENGTH];

        result = normalize(queue, (uint16_t) (offset + baton_queue_record_size(length)), tail);
    }

    return result;
}

uint16_t baton_queue_used(const struct baton_queue* queue)
{
    uint16_t used = 0U;

    if (queue != NULL)
    {
        const uint16_t head = queue->head;
        const uint16_t tail = queue->tail;

        if (tail >= head)
        {
            used = (uint16_t) (tail - head);
        }
        else
        {
            used = (uint16_t) ((uint16_t) (queue->size - head) + tail);
        }
    }

    return used;
}

void baton_queue_read(const struct baton_queue* queue, uint16_t offset, struct baton_record* record)
{
    if ((queue != NULL) && (record != NULL))
    {
        const uint8_t* header = &queue->memory[offset];

        record->pending = baton_get_u32le(&header[REC_PENDING]);
        record->inflight = baton_get_u32le(&header[REC_INFLIGHT]);
        record->stamp = baton_get_u32le(&header[REC_STAMP]);
        record->msg_id = header[REC_MSG_ID];
        record->length = header[REC_LENGTH];
        record->dst = header[REC_DST];
        record->flags = header[REC_FLAGS];
    }
}

void baton_queue_write(struct baton_queue* queue, uint16_t offset,
                       const struct baton_record* record)
{
    if ((queue != NULL) && (record != NULL))
    {
        uint8_t* header = &queue->memory[offset];

        baton_put_u32le(&header[REC_PENDING], record->pending);
        baton_put_u32le(&header[REC_INFLIGHT], record->inflight);
        baton_put_u32le(&header[REC_STAMP], record->stamp);
        header[REC_MSG_ID] = record->msg_id;
        header[REC_LENGTH] = record->length;
        header[REC_DST] = record->dst;
        header[REC_FLAGS] = record->flags;
    }
}

void baton_queue_set_masks(struct baton_queue* queue, uint16_t offset, uint32_t pending,
                           uint32_t inflight)
{
    if (queue != NULL)
    {
        baton_put_u32le(&queue->memory[offset + REC_PENDING], pending);
        baton_put_u32le(&queue->memory[offset + REC_INFLIGHT], inflight);
    }
}

void baton_queue_set_flags(struct baton_queue* queue, uint16_t offset, uint8_t flags)
{
    if (queue != NULL)
    {
        queue->memory[offset + REC_FLAGS] = flags;
    }
}

uint8_t* baton_queue_data(struct baton_queue* queue, uint16_t offset)
{
    uint8_t* data = NULL;

    if (queue != NULL)
    {
        data = &queue->memory[offset + BATON_REC_HEADER_SIZE];
    }

    return data;
}