
#include "os/os_thread.h"
#include "pipe/p_defines.h"
#include "util/u_ringbuffer.h"
#include "util/u_math.h"
#include "util/u_memory.h"

/* Generic ringbuffer: 
 */
struct util_ringbuffer 
{
   struct util_packet *buf;
   unsigned mask;

   /* Can this be done with atomic variables??
    */
   unsigned head;
   unsigned tail;
   cnd_t change;
   mtx_t mutex;
};


struct util_ringbuffer *util_ringbuffer_create( unsigned dwords )
{
   struct util_ringbuffer *ring = CALLOC_STRUCT(util_ringbuffer);
   if (!ring)
      return NULL;

   assert(util_is_power_of_two_or_zero(dwords));
   
   ring->buf = MALLOC( dwords * sizeof(unsigned) );
   if (ring->buf == NULL)
      goto fail;

   ring->mask = dwords - 1;

   cnd_init(&ring->change);
   (void) mtx_init(&ring->mutex, mtx_plain);
   return ring;

fail:
   FREE(ring->buf);
   FREE(ring);
   return NULL;
}

void util_ringbuffer_destroy( struct util_ringbuffer *ring )
{
   cnd_destroy(&ring->change);
   mtx_destroy(&ring->mutex);
   FREE(ring->buf);
   FREE(ring);
}

/**
 * Return number of free entries in the ring
 */
static inline unsigned util_ringbuffer_space( const struct util_ringbuffer *ring )
{
   return (ring->tail - (ring->head + 1)) & ring->mask;
}

/**
 * Is the ring buffer empty?
 */
static inline boolean util_ringbuffer_empty( const struct util_ringbuffer *ring )
{
   return util_ringbuffer_space(ring) == ring->mask;
}

void util_ringbuffer_enqueue( struct util_ringbuffer *ring,
                              const struct util_packet *packet )
{
   unsigned i;

   /* XXX: over-reliance on mutexes, etc:
    */
   mtx_lock(&ring->mutex);

   /* make sure we don't request an impossible amount of space
    */
   assert(packet->dwords <= ring->mask);

   /* Wait for free space:
    */
   while (util_ringbuffer_space(ring) < packet->dwords)
      cnd_wait(&ring->change, &ring->mutex);

   /* Copy data to ring:
    */
   for (i = 0; i < packet->dwords; i++) {

      /* Copy all dwords of the packet.  Note we're abusing the
       * typesystem a little - we're being passed a pointer to
       * something, but probably not an array of packet structs:
       */
      ring->buf[ring->head] = packet[i];
      ring->head++;
      ring->head &= ring->mask;
   }

   /* Signal change:
    */
   cnd_signal(&ring->change);
   mtx_unlock(&ring->mutex);
}

enum pipe_error util_ringbuffer_dequeue( struct util_ringbuffer *ring,
                                         struct util_packet *packet,
                                         unsigned max_dwords,
                                         boolean wait )
{
   const struct util_packet *ring_packet;
   unsigned i;
   int ret = PIPE_OK;

   /* XXX: over-reliance on mutexes, etc:
    */
   mtx_lock(&ring->mutex);

   /* Get next ring entry:
    */
   if (wait) {
      while (util_ringbuffer_empty(ring))
         cnd_wait(&ring->change, &ring->mutex);
   }
   else {
      if (util_ringbuffer_empty(ring)) {
         ret = PIPE_ERROR_OUT_OF_MEMORY;
         goto out;
      }
   }

   ring_packet = &ring->buf[ring->tail];

   /* Both of these are considered bugs.  Raise an assert on debug builds.
    */
   if (ring_packet->dwords > ring->mask + 1 - util_ringbuffer_space(ring) ||
       ring_packet->dwords > max_dwords) {
      assert(0);
      ret = PIPE_ERROR_BAD_INPUT;
      goto out;
   }

   /* Copy data from ring:
    */
   for (i = 0; i < ring_packet->dwords; i++) {
      packet[i] = ring->buf[ring->tail];
      ring->tail++;
      ring->tail &= ring->mask;
   }

out:
   /* Signal change:
    */
   cnd_signal(&ring->change);
   mtx_unlock(&ring->mutex);
   return ret;
}
