/*
 *  Copyright (c) 2018, Sam Kumar <samkumar@cs.berkeley.edu>
 *  Copyright (c) 2018, University of California, Berkeley
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CBUF_H_
#define CBUF_H_

/* CIRCULAR BUFFER */

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

struct otLinkedBuffer;

/* Represents a circular buffer. */
struct cbufhead {
    size_t r_index;
    size_t used;
    size_t size;
    uint8_t* buf;
};

/* Initializes a circular buffer (chdr is the header storing metadata). */
void cbuf_init(struct cbufhead* chdr, uint8_t* buf, size_t len);

/* Function type for copying data into or out of a circular buffer. */
typedef void(*cbuf_copier_t)(void*, size_t, const void*, size_t, size_t);

/* Instantiations of cbuf_copier_t, for copying data between circular buffers and regular buffers. */
void cbuf_copy_into_buffer(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes);
void cbuf_copy_from_buffer(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes);

/* Instantiations of cbuf_copier_t, for copying data between circular buffers and otMessages. */
void cbuf_copy_into_message(void* buffer, size_t buffer_offset, const void* arr, size_t arr_offset, size_t num_bytes);
void cbuf_copy_from_message(void* arr, size_t arr_offset, const void* buffer, size_t buffer_offset, size_t num_bytes);

/* Writes data to the back of the circular buffer using the specified copier. */
size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from);

/* Reads data from the front of the circular buffer using the specified copier. */
size_t cbuf_read(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into);

/* Reads data at the specified offset, in bytes, from the front of the circular buffer using the specified copier. */
size_t cbuf_read_offset(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, size_t offset, cbuf_copier_t copy_into);

/* Drops bytes from the front of the circular buffer. */
size_t cbuf_pop(struct cbufhead* chdr, size_t numbytes);

/* Returns the number of bytes available for reading from the circular buffer. */
size_t cbuf_used_space(struct cbufhead* chdr);

/* Returns the number of bytes that can be written to the circular buffer before it is full. */
size_t cbuf_free_space(struct cbufhead* chdr);

/* Returns the total capacity of the circular buffer, in bytes. */
size_t cbuf_size(struct cbufhead* chdr);

/* Returns true if the circular buffer is empty, and false if it is not empty. */
bool cbuf_empty(struct cbufhead* chdr);

/* Rotates the circular buffer's data so that the "used" portion begins at the beginning of the buffer. */
void cbuf_contiguify(struct cbufhead* chdr, uint8_t* bitmap);

/* Populates the provided otLinkedBuffers to reference the data currently in the circular buffer. */
void cbuf_reference(const struct cbufhead* chdr, struct otLinkedBuffer* first, struct otLinkedBuffer* second);

/* Writes DATA at the end of the circular buffer without making it available for
   reading. This data is said to be "out-of-sequence". OFFSET is position at
   which to write these bytes, relative to the position where cbuf_write would
   write them. Each bit in the BITMAP corresponds to a byte in the circular
   buffer; the bits corresponding to the bytes containing the newly written
   data are set. The index of the first byte written is stored into FIRSTINDEX,
   if it is not NULL. */
size_t cbuf_reass_write(struct cbufhead* chdr, size_t offset, const void* data, size_t data_offset, size_t numbytes, uint8_t* bitmap, size_t* firstindex, cbuf_copier_t copy_from);

/* Makes NUMBYTES out-of-seqence bytes available for reading in the circular
   buffer. No data is copied; the out-of-sequence bytes made available are the
   bytes currently at the position where cbuf_write would write them. The bytes
   are taken from the unused space of the buffer, and can be set using
   cbuf_reass_write. */
size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap);

/* Counts the number of contiguous out-of-sequence bytes at the specified
   OFFSET, until the count reaches the specified LIMIT. Note that, for a given,
   limit, this function might overcount the length of the continuous
   out-of-sequence bytes and return a greater number; the caller is assumed to
   handle this appropriately (i.e., treating the limit not as a hard upper
   bound on the return value, but rather as, "I don't care if more bits than
   this are set"). Just because the function returns something more than
   LIMIT, it doesn't necessarily mean that more than LIMIT bits are actually
   set. Note that LIMIT should never be set to a value greater than the number
   of bytes in the circular buffer. */
size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit);

/* Returns a true value iff INDEX is the index of a byte within OFFSET bytes
   past the end of the buffer. */
int cbuf_reass_within_offset(struct cbufhead* chdr, size_t offset, size_t index);

#endif
