/*
 * Tentun - covert tunneling project
 *
 * Copyright (C) 2003 David Gordon
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 */

#include <assert.h>
#include "rbuf.h"


void set_size(rbuf *rb, int new_size)
{
    // Change the size of the buffer to new_size. The content of the buffer
    // is lost, and the buffer behaves as it has just been created.
    // Arguments:
    // New size.
    assert(new_size >= 0);

    clean_up(rb);

    // We allocate one more slot than requested for housekeeping.
    rb->alloc_size = new_size + 1;
    rb->read_index = 0;
    rb->write_index = 0;

    rb->buf = (char *)malloc(sizeof(char)*rb->alloc_size);
}

void clean_up(rbuf *rb)
{
    free(rb->buf);
}

int is_empty(rbuf *rb)
{
    // This function returns true if the buffer is empty.
    return (rb->read_index == rb->write_index);
}

int is_full(rbuf *rb)
{
    // This function returns true if the buffer is full.
    return (avail_size(rb) == 0);
}

int avail_size(rbuf *rb)
{
    // This function returns the size of space available for writing in the
    // buffer.
    int gap = rb->read_index - rb->write_index;

    if (gap <= 0) {
	gap += rb->alloc_size;
    }

    return (gap - 1);
}

int used_size(rbuf *rb)
{
    // This function returns the size of the space used by the data in the
    // buffer.
    return (rb->alloc_size - avail_size(rb) - 1);
}

void seek(rbuf *rb, int pos)
{
    // This function advances the read pointer of 'size' bytes. It is the
    // responsability of the user to make sure that enough data is 
    // available for reading the data buffer.
    assert(used_size(rb) >= abs(pos));

    rb->read_index = (rb->read_index + pos) % rb->alloc_size;
}

void internal_read_buf(rbuf *rb, char *data_buf, int data_buf_size,
				  int *local_index_ptr)
{
    // Since the code for read_buf and peek_buf is mostly the same, we use
    // this function to do the job and use the previous functions
    // as front-end. It is the responsability of the user to make sure that
    // enough data is available for reading the data buffer.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    // Pointer to the read index after the read is completed.

    int remaining_size = data_buf_size;
    int local_index = rb->read_index;
    int read_size = rb->alloc_size - local_index;

    assert(used_size(rb) >= abs(data_buf_size));

    // Calculate how many bytes may be read before we reach the right end
    // of the buffer.

    if (read_size > data_buf_size) {
	read_size = data_buf_size;
    }

    if (read_size) {
	memcpy(data_buf, rb->buf + local_index, read_size);
	remaining_size -= read_size;
	local_index += read_size;
    }
    // Roll over.
    if (local_index == rb->alloc_size) {
	local_index = 0;
    }
    // Finished.
    if (remaining_size == 0) {
	*local_index_ptr = local_index;
	return;
    }
    // Copy the rest of the buffer.
    memcpy(data_buf + read_size, rb->buf + local_index, remaining_size);

    // Assing the new read index.
    local_index += remaining_size;
    *local_index_ptr = local_index;
}


void read_buf(rbuf *rb, char *data_buf, int data_buf_size)
{
    // Read data_buf from the round buffer. data_buf is assumed to be
    // data_buf_size long. It is the responsability of the user to make 
    // sure that enough data is available for reading the data buffer.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
  internal_read_buf(rb, data_buf, data_buf_size, &(rb->read_index));
}

void peek_buf(rbuf *rb, char *data_buf, int data_buf_size)
{
    // Read data_buf from the round buffer. data_buf is assumed to be
    // data_buf_size long. It is the responsability of the user to make 
    // sure that enough data is available for reading the data buffer. The
    // data read won't be removed from the buffer and will be still
    // available for the next call.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    int dummy;

    internal_read_buf(rb, data_buf, data_buf_size, &dummy);
}

void write_buf(rbuf *rb, char *data_buf, int data_buf_size)
{
    // Write data_buf inside the round buffer. data_buf is assumed to be
    // data_buf_size long. It is the responsability of the user to 
    // make sure that enough space is available for writing the data
    // buffer.
    // Arguments:
    // Data buffer.
    // Size of the data buffer.
    int remaining_size = data_buf_size;
    int write_size = rb->alloc_size - rb->write_index;

    assert(avail_size(rb) >= abs(data_buf_size));

    // Calculate how many bytes may be written before we reach the right
    // end of the buffer.

    if (write_size > data_buf_size) {
	write_size = data_buf_size;
    }

    if (write_size) {
	memcpy(rb->buf + rb->write_index, data_buf, write_size);
	remaining_size -= write_size;
	rb->write_index += write_size;
    }
    // Roll over.
    if (rb->write_index == rb->alloc_size) {
	rb->write_index = 0;
    }
    // Finished.
    if (remaining_size == 0) {
	return;
    }
    // Copy the rest of the buffer.
    memcpy(rb->buf + rb->write_index, data_buf + write_size, remaining_size);

    rb->write_index += remaining_size;
}
