/* ---------------------------------------------------------------------
   (c) ED 2003
   Project      : CLIB
   Function     : Resource Allocator
   Module       : RA
   File         : ra.c
   Created      : 17-09-2003
   Modified     : 23-03-2004
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Log

   0.0 17-09-2003 Created
   1.0 17-09-2003 Initial version
   1.1 17-09-2003 Error counters added
   1.2 18-09-2003 Ranges modified:
   .              - Data from 0 to 255
   .              - Resource from 1 to 255. (0 = not allocated or freed)
   1.3 18-09-2003 Interface of ra_free() modified so that the
   .              function updates the resource to RA_RES_FREED.
   1.4 19-09-2003 ra_free_all() added
   1.5 22-09-2003 Return code debug. Compliance with the specs
   2.0 23-09-2003 Redesigned. Resource is now internal only
   2.1 24-09-2003 DBG driven trace added
   2.2 25-09-2003 minimum counter added
   2.3 25-09-2003 The ra_alloc function returns the resource number
   2.4 25-09-2003 Optional identifier added to ra_init()
   2.5 01-10-2003 Ranges modified:
   .              - Data from 0 to 65535
   2.6 07-10-2003 Added function ra_get_free()
   2.7 23-03-2004 Ranges modified:
   .              - Data from 0 to 0xFFFFFFFF

   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This is not C++. Please use a C compiler.
#endif

#ifdef RA_DBG
#error 'ra_dbg.h' should not be used in the implementation.
#endif

#include "ed/inc/ra.h"
#include "ed/inc/sysalloc.h"
#include "ed/inc/sys.h"

/* private macro definitions =========================================== */

#define ID "RA"
#define VER "2.7"

#define DBG 0

#if DBG
#define MODULE ID "."
#else
#undef PRINTF
#define PRINTF(a)
#endif

/* private constants =================================================== */

enum
{
   RES_FREED = 0,
   RES_MIN = 1,
   RES_MAX = 255,

   dummy
};

/* private types ======================================================= */

/* private structures ================================================== */

/* private functions =================================================== */

/* ---------------------------------------------------------------------
   clr_res ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static void clr_res (ra_s * const this)
{
   if (this != NULL)
   {
      size_t i;

      for (i = 0; i < this->nb_res; i++)
      {
         ra_usr_s *p = this->p_res + i;

         p->res = i + RES_MIN;
         p->used = 0;
         p->data = 0;
      }
   }
}

/* ---------------------------------------------------------------------
   init ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static void init (ra_s * const this
                  ,size_t const nb_res)
{
   this->nb_res = nb_res;
   this->count = nb_res;
   this->min_count = this->count;

   clr_res (this);
}

/* ---------------------------------------------------------------------
   get_pres_from_data ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static ra_usr_s *get_pres_from_data (ra_s const *const this
                                     ,ulong const data)
{
   ra_usr_s *p_res = NULL;
   size_t i;

   for (i = 0; i < this->nb_res; i++)
   {
      ra_usr_s *const p = this->p_res + i;

      if (p->used && p->data == data)
      {
         p_res = p;
         break;
      }
   }
   return p_res;
}

/* ---------------------------------------------------------------------
   get_pres_from_res ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static ra_usr_s *get_pres_from_res (ra_s const *const this
                                    ,uint const res)
{
   ra_usr_s *p_res = NULL;
   size_t i;

   for (i = 0; i < this->nb_res; i++)
   {
      ra_usr_s *p = this->p_res + i;

      if (p->used && p->res == res)
      {
         p_res = p;
         break;
      }
   }
   return p_res;
}

/* ---------------------------------------------------------------------
   get_res ()
   ---------------------------------------------------------------------
   what is the resource of this data?
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static uchar get_res (ra_s const *const this
                      ,ulong const data)
{
   uchar res = RES_FREED;
   ra_usr_s *p = get_pres_from_data (this, data);

   if (p)
   {
      res = (uchar) p->res;
   }
   return res;
}

/* ---------------------------------------------------------------------
   get_data ()
   ---------------------------------------------------------------------
   what is the data of this resource?
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static long get_data (ra_s const *const this
                      ,uint const res)
{
   long data = -1;
   ra_usr_s *p = get_pres_from_res (this, res);

   if (p)
   {
      data = p->data;
   }
   return data;
}

/* ---------------------------------------------------------------------
   is_allocated ()
   ---------------------------------------------------------------------
   is the resource already allocated?
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static int is_allocated (ra_s const *const this
                         ,ulong const data
                         ,uchar * p_res)
{
   ra_usr_s *p = get_pres_from_data (this, data);

   if (p)
   {
      if (p_res)
      {
         *p_res = (uchar) p->res;
      }
   }
   return p != NULL;
}

/* ---------------------------------------------------------------------
   allocate ()
   ---------------------------------------------------------------------
   allocate a resource
   ---------------------------------------------------------------------
   I:
   O: 0-n or -1 on error (full)
   --------------------------------------------------------------------- */
static int allocate (ra_s * const this, ulong const data)
{
   int res = -1;

   if (this->count != 0)
   {
      size_t i;

      for (i = 0; i < this->nb_res; i++)
      {
         ra_usr_s *const p = this->p_res + i;

         if (!p->used)
         {
            p->data = data;
            p->used = 1;
            res = p->res;
            this->count--;

            if (this->count < this->min_count)
            {
               this->min_count = this->count;
            }

            PRINTF ((MODULE "allocate() data=%u res=%u" EOL, data, res));

            break;
         }
      }
   }
   return res;
}

/* public internal functions =========================================== */

/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   ra_sid ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *ra_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   ra_sver ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *ra_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   ra_create ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_s *ra_create (size_t const nb_res)
{
   ra_s *this = malloc (sizeof *this);
   if (this != NULL)
   {
      CLR (this, ra_s);

      this->p_res = malloc (sizeof *this->p_res * nb_res);

      if (this->p_res)
      {
         init (this, nb_res);
      }

   }
   return this;
}

/* ---------------------------------------------------------------------
   ra_delete ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
void ra_delete (ra_s * this)
{
   if (this != NULL)
   {
      FREE (this->p_res);
      free (this);
   }
}

/* ---------------------------------------------------------------------
   ra_clr ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_clr (ra_s * const this)
{
   ra_err_e err = RA_OK;

   if (this != NULL)
   {
      CLR (this, ra_s);
   }
   else
   {
      err = RA_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   ra_init ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_init (ra_s * const this
                  ,ra_usr_s * const p_res
                  ,size_t const nb_res
                  ,char const *sid
)
{
   ra_err_e err = RA_OK;

   if (this != NULL)
   {
      if (p_res != NULL)
      {
         if (sid)
         {
            this->sid = sid;
         }
         else
         {
            this->sid = ra_sid ();
         }

         if (nb_res >= RES_MIN && nb_res <= RA_SIZE_MAX)
         {
            /* static */
            this->p_res = p_res;
            init (this, nb_res);
         }
         else
         {
            err = RA_ERR_SIZE;
         }
      }
   }
   else
   {
      err = RA_ERR_CONTEXT;
   }
   return err;
}

#if RA_OUT
/* ---------------------------------------------------------------------
   ra_install_out ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_install_out (ra_s * const this
                         ,ra_out_f * const pf
                         ,void *const p_usr)
{
   ra_err_e err = RA_OK;

   if (this != NULL)
   {
      this->pf = pf;
      this->p_usr = p_usr;
   }
   else
   {
      err = RA_ERR_CONTEXT;
   }
   return err;
}
#endif

#if 0
/* ---------------------------------------------------------------------
   ra_in ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_in (ra_s * const this, int const data)
{
   ra_err_e err = RA_OK;

   if (this != NULL)
   {
      if (this->pf != NULL)
      {
         int ret = (*this->pf) (this->p_usr, data);
         if (ret != 0)

         {
            err = RA_ERR_CB_OUT;
         }
      }
   }
   else
   {
      err = RA_ERR_CONTEXT;
   }
   return err;
}
#endif

/* ---------------------------------------------------------------------
   ra_alloc ()
   ---------------------------------------------------------------------
   Allocation of a resource
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_alloc (ra_s * const this
                   ,ulong const data
                   ,uchar * const p_res)
{
   ra_err_e err = RA_OK;

   if (data <= RA_DATA_MAX)
   {
      if (this != NULL)
      {
         uchar res;

         if (!is_allocated (this, data, &res))
         {
            res = allocate (this, data);

            if (res == (uchar) - 1)
            {
               err = RA_ERR_NO_MORE_RESOURCES;
            }
            else
            {
               if (p_res != NULL)
               {
                  *p_res = (uchar) res;
               }
            }
         }
         else
         {
            this->err.alloc++;
            err = RA_ERR_ALREADY_ALLOCATED;

            if (p_res != NULL)
            {
               *p_res = res;
            }
         }
      }
      else
      {
         err = RA_ERR_CONTEXT;
      }
   }
   else
   {
      err = RA_ERR_DATA_VALUE;
   }
   return err;
}

/* ---------------------------------------------------------------------
   ra_free ()
   ---------------------------------------------------------------------
   Deallocation of a resource
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_free (ra_s * const this, ulong const data)
{
   ra_err_e err = RA_OK;

   if (data <= RA_DATA_MAX)
   {
      if (this != NULL)
      {
         if (this->count < this->nb_res)
         {
            ra_usr_s *const p = get_pres_from_data (this, data);

            if (p->used)
            {
               p->used = 0;
               this->count++;

               PRINTF ((MODULE "free() data=%u res=%u" EOL, data, p->res));
            }
            else
            {
               this->err.free++;
               err = RA_ERR_NEVER_ALLOCATED;
            }
         }
         else
         {
            this->err.free++;
            err = RA_ERR_NEVER_ALLOCATED;
         }
      }
      else
      {
         err = RA_ERR_CONTEXT;
      }
   }
   else
   {
      err = RA_ERR_DATA_VALUE;
   }
   return err;
}

/* ---------------------------------------------------------------------
   ra_free_all ()
   ---------------------------------------------------------------------
   Deallocation of all resources
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_free_all (ra_s * const this)
{
   ra_err_e err = RA_OK;

   if (this != NULL)
   {
      this->count = this->nb_res;

      clr_res (this);

      this->err.alloc = 0;
      this->err.free = 0;
   }
   else
   {
      err = RA_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   ra_get_data ()
   ---------------------------------------------------------------------
   getting data from resource
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_get_data (ra_s * const this
                      ,uchar const res
                      ,ulong * const p_data)
{
   ra_err_e err = RA_OK;

   if (p_data)
   {
      if (this != NULL)
      {
         if (res >= RES_MIN && res <= this->nb_res)
         {
            ulong data = get_data (this, res);

            if (data != (ulong) - 1)
            {
               *p_data = data;
            }
            else
            {
               err = RA_ERR_NEVER_ALLOCATED;
            }
         }
         else
         {
            err = RA_ERR_RESOURCE_VALUE;
         }
      }
      else
      {
         err = RA_ERR_CONTEXT;
      }
   }
   else
   {
      err = RA_ERR_DATA_ADDRESS;
   }
   return err;
}

/* ---------------------------------------------------------------------
   ra_get_res ()
   ---------------------------------------------------------------------
   getting resource from data
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_get_res (ra_s * const this
                     ,uchar * const p_res
                     ,ulong const data)
{
   ra_err_e err = RA_OK;

   if (p_res)
   {
      if (data <= RA_DATA_MAX)
      {
         if (this != NULL)
         {
            int res = get_res (this, data);

            if (res != RES_FREED)
            {
               if (res >= RES_MIN && res <= RES_MAX)
               {
                  *p_res = (uchar) res;
               }
               else
               {
                  err = RA_ERR_INCONSISTENT_RESOURCE;
               }
            }
            else
            {
               *p_res = (uchar) res;
               err = RA_ERR_NEVER_ALLOCATED;
            }
         }
         else
         {
            err = RA_ERR_CONTEXT;
         }
      }
      else
      {
         err = RA_ERR_DATA_VALUE;
      }
   }
   else
   {
      err = RA_ERR_RESOURCE_ADDRESS;
   }
   return err;
}

/* ---------------------------------------------------------------------
   ra_get_free ()
   ---------------------------------------------------------------------
   how many free places ?
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
ra_err_e ra_get_free (ra_s * const this
                      ,uint * p_free)
{
   ra_err_e err = RA_OK;

   if (this)
   {
      if (p_free)
      {
         *p_free = this->count;
      }
      else
      {
         err = RA_ERR_NB_FREE_ADDRESS;
      }
   }
   else
   {
      err = RA_ERR_RESOURCE_ADDRESS;
   }
   return err;
}

/* public data ========================================================= */

/* ---------------------------------------------------------------------
   Generated by NEW (c) ED 2.1
   Powered by C-code generator (c) ED 2003 1.0
   --------------------------------------------------------------------- */
