///
/// @file ringbuffer.c	@brief Ringbuffer module
///
/// Copyright (c) 2009, 2011, 2014  by Johns.  All Rights Reserved.
///
/// Contributor(s):
///
/// License: AGPLv3
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
/// published by the Free Software Foundation, either version 3 of the
/// License.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/// GNU Affero General Public License for more details.
///
/// $Id: c9497b197ce7e7a6ba397944edc7ccb161152efd $
//////////////////////////////////////////////////////////////////////////////

///
/// @defgroup Ringbuffer The ring buffer module.
///
/// Lock free ring buffer with only one writer and one reader.
///

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "iatomic.h"
#include "ringbuffer.h"

/// ring buffer structure
struct _ring_buffer_ {
    char *Buffer;          ///< ring buffer data
    const char *BufferEnd; ///< end of buffer
    size_t Size;           ///< bytes in buffer (for faster calc)

    const char *ReadPointer; ///< only used by reader
    char *WritePointer;      ///< only used by writer

    /// The only thing modified by both
    atomic_t Filled; ///< how many of the buffer is used
};

/**
**	Reset ring buffer pointers.
**
**	@param rb	Ring buffer to reset read/write pointers.
*/
void RingBufferReset(RingBuffer *rb) {
    rb->ReadPointer = rb->Buffer;
    rb->WritePointer = rb->Buffer;
    atomic_set(&rb->Filled, 0);
}

/**
**	Allocate a new ring buffer.
**
**	@param size	Size of the ring buffer.
**
**	@returns	Allocated ring buffer, must be freed with
**			RingBufferDel(), NULL for out of memory.
*/
RingBuffer *RingBufferNew(size_t size) {
    RingBuffer *rb;

    if (!(rb = malloc(sizeof(*rb)))) { // allocate structure
        return rb;
    }
    if (!(rb->Buffer = malloc(size))) { // allocate buffer
        free(rb);
        return NULL;
    }

    rb->Size = size;
    rb->BufferEnd = rb->Buffer + size;
    RingBufferReset(rb);

    return rb;
}

/**
**	Free an allocated ring buffer.
*/
void RingBufferDel(RingBuffer *rb) {
    free(rb->Buffer);
    free(rb);
}

/**
**	Advance write pointer in ring buffer.
**
**	@param rb	Ring buffer to advance write pointer.
**	@param cnt	Number of bytes to be adavanced.
**
**	@returns	Number of bytes that could be advanced in ring buffer.
*/
size_t RingBufferWriteAdvance(RingBuffer *rb, size_t cnt) {
    size_t n;

    n = rb->Size - atomic_read(&rb->Filled);
    if (cnt > n) { // not enough space
        cnt = n;
    }
    //
    //	Hitting end of buffer?
    //
    n = rb->BufferEnd - rb->WritePointer;
    if (n > cnt) { // don't cross the end
        rb->WritePointer += cnt;
    } else { // reached or cross the end
        rb->WritePointer = rb->Buffer;
        if (n < cnt) {
            n = cnt - n;
            rb->WritePointer += n;
        }
    }

    //
    //	Only atomic modification!
    //
    atomic_add(cnt, &rb->Filled);
    return cnt;
}

/**
**	Write to a ring buffer.
**
**	@param rb	Ring buffer to write to.
**	@param buf	Buffer of @p cnt bytes.
**	@param cnt	Number of bytes in buffer.
**
**	@returns	The number of bytes that could be placed in the ring
**			buffer.
*/
size_t RingBufferWrite(RingBuffer *rb, const void *buf, size_t cnt) {
    size_t n;

    n = rb->Size - atomic_read(&rb->Filled);
    if (cnt > n) { // not enough space
        cnt = n;
    }
    //
    //	Hitting end of buffer?
    //
    n = rb->BufferEnd - rb->WritePointer;
    if (n > cnt) { // don't cross the end
        memcpy(rb->WritePointer, buf, cnt);
        rb->WritePointer += cnt;
    } else { // reached or cross the end
        memcpy(rb->WritePointer, buf, n);
        rb->WritePointer = rb->Buffer;
        if (n < cnt) {
            buf += n;
            n = cnt - n;
            memcpy(rb->WritePointer, buf, n);
            rb->WritePointer += n;
        }
    }

    //
    //	Only atomic modification!
    //
    atomic_add(cnt, &rb->Filled);
    return cnt;
}

/**
**	Get write pointer and free bytes at this position of ring buffer.
**
**	@param rb	Ring buffer to write to.
**	@param[out] wp	Write pointer is placed here
**
**	@returns	The number of bytes that could be placed in the ring
**			buffer at the write pointer.
*/
size_t RingBufferGetWritePointer(RingBuffer *rb, void **wp) {
    size_t n;
    size_t cnt;

    //	Total free bytes available in ring buffer
    cnt = rb->Size - atomic_read(&rb->Filled);

    *wp = rb->WritePointer;

    //
    //	Hitting end of buffer?
    //
    n = rb->BufferEnd - rb->WritePointer;
    if (n <= cnt) { // reached or cross the end
        return n;
    }
    return cnt;
}

/**
**	Advance read pointer in ring buffer.
**
**	@param rb	Ring buffer to advance read pointer.
**	@param cnt	Number of bytes to be advanced.
**
**	@returns	Number of bytes that could be advanced in ring buffer.
*/
size_t RingBufferReadAdvance(RingBuffer *rb, size_t cnt) {
    size_t n;

    n = atomic_read(&rb->Filled);
    if (cnt > n) { // not enough filled
        cnt = n;
    }
    //
    //	Hitting end of buffer?
    //
    n = rb->BufferEnd - rb->ReadPointer;
    if (n > cnt) { // don't cross the end
        rb->ReadPointer += cnt;
    } else { // reached or cross the end
        rb->ReadPointer = rb->Buffer;
        if (n < cnt) {
            n = cnt - n;
            rb->ReadPointer += n;
        }
    }

    //
    //	Only atomic modification!
    //
    atomic_sub(cnt, &rb->Filled);
    return cnt;
}

/**
**	Read from a ring buffer.
**
**	@param rb	Ring buffer to read from.
**	@param buf	Buffer of @p cnt bytes.
**	@param cnt	Number of bytes to be read.
**
**	@returns	Number of bytes that could be read from ring buffer.
*/
size_t RingBufferRead(RingBuffer *rb, void *buf, size_t cnt) {
    size_t n;

    n = atomic_read(&rb->Filled);
    if (cnt > n) { // not enough filled
        cnt = n;
    }
    //
    //	Hitting end of buffer?
    //
    n = rb->BufferEnd - rb->ReadPointer;
    if (n > cnt) { // don't cross the end
        memcpy(buf, rb->ReadPointer, cnt);
        rb->ReadPointer += cnt;
    } else { // reached or cross the end
        memcpy(buf, rb->ReadPointer, n);
        rb->ReadPointer = rb->Buffer;
        if (n < cnt) {
            buf += n;
            n = cnt - n;
            memcpy(buf, rb->ReadPointer, n);
            rb->ReadPointer += n;
        }
    }

    //
    //	Only atomic modification!
    //
    atomic_sub(cnt, &rb->Filled);
    return cnt;
}

/**
**	Get read pointer and used bytes at this position of ring buffer.
**
**	@param rb	Ring buffer to read from.
**	@param[out] rp	Read pointer is placed here
**
**	@returns	The number of bytes that could be read from the ring
**			buffer at the read pointer.
*/
size_t RingBufferGetReadPointer(RingBuffer *rb, const void **rp) {
    size_t n;
    size_t cnt;

    //	Total used bytes in ring buffer
    cnt = atomic_read(&rb->Filled);

    *rp = rb->ReadPointer;

    //
    //	Hitting end of buffer?
    //
    n = rb->BufferEnd - rb->ReadPointer;
    if (n <= cnt) { // reached or cross the end
        return n;
    }
    return cnt;
}

/**
**	Get free bytes in ring buffer.
**
**	@param rb	Ring buffer.
**
**	@returns	Number of bytes free in buffer.
*/
size_t RingBufferFreeBytes(RingBuffer *rb) { return rb->Size - atomic_read(&rb->Filled); }

/**
**	Get used bytes in ring buffer.
**
**	@param rb	Ring buffer.
**
**	@returns	Number of bytes used in buffer.
*/
size_t RingBufferUsedBytes(RingBuffer *rb) { return atomic_read(&rb->Filled); }