/**
 * \addtogroup exampleapps
 * @{
 */

/**
 * \file
 * Memory block allocation routines.
 * \author Adam Dunkels <adam@sics.se>
 *
 * The memory block allocation routines provide a simple yet powerful
 * set of functions for managing a set of memory blocks of fixed
 * size. A set of memory blocks is statically declared with the
 * MEMB() macro. Memory blocks are allocated from the declared
 * memory by the memb_alloc() function, and are deallocated with the
 * memb_free() function.
 *
 * \note Because of namespace clashes only one MEMB() can be
 * declared per C module, and the name scope of a MEMB() memory
 * block is local to each C module.
 *
 * The following example shows how to declare and use a memory block
 * called "cmem" which has 8 chunks of memory with each memory chunk
 * being 20 bytes large.
 *
 \code
 MEMB(cmem, 20, 8);

 int main(int argc, char *argv[]) {
    char *ptr;
    
    memb_init(&cmem);

    ptr = memb_alloc(&cmem);

    if(ptr != NULL) {
       do_something(ptr);
    } else {
       printf("Could not allocate memory.\n");
    }

    if(memb_free(ptr) == 0) {
       printf("Deallocation succeeded.\n");
    }
 }
 \endcode
 * 
 */

#include <string.h>

#include "memb.h"

/*------------------------------------------------------------------------------*/
/**
 * Initialize a memory block that was declared with MEMB().
 *
 * \param m A memory block previosly declared with MEMB().
 */
/*------------------------------------------------------------------------------*/
void
memb_init(struct memb_blocks *m)
{
  memset(m->mem, (m->size + 1) * m->num, 0);
}
/*------------------------------------------------------------------------------*/
/**
 * Allocate a memory block from a block of memory declared with MEMB().
 *
 * \param m A memory block previosly declared with MEMB().
 */
/*------------------------------------------------------------------------------*/
char *
memb_alloc(struct memb_blocks *m)
{
  int i;
  char *ptr;

  ptr = m->mem;
  for(i = 0; i < m->num; ++i) {
    if(*ptr == 0) {
      /* If this block was unused, we increase the reference count to
	 indicate that it now is used and return a pointer to the
	 first byte following the reference counter. */
      ++*ptr;
      return ptr + 1;
    }
    ptr += m->size + 1;
  }

  /* No free block was found, so we return NULL to indicate failure to
     allocate block. */
  return NULL;
}
/*------------------------------------------------------------------------------*/
/**
 * Deallocate a memory block from a memory block previously declared
 * with MEMB().
 *
 * \param m m A memory block previosly declared with MEMB().
 *
 * \param ptr A pointer to the memory block that is to be deallocated.
 *
 * \return The new reference count for the memory block (should be 0
 * if successfully deallocated) or -1 if the pointer "ptr" did not
 * point to a legal memory block.
 */
/*------------------------------------------------------------------------------*/
char
memb_free(struct memb_blocks *m, char *ptr)
{
  int i;
  char *ptr2;

  /* Walk through the list of blocks and try to find the block to
     which the pointer "ptr" points to. */
  ptr2 = m->mem;
  for(i = 0; i < m->num; ++i) {
    
    if(ptr2 == ptr - 1) {
      /* We've found to block to which "ptr" points so we decrease the
	 reference count and return the new value of it. */      
      return --*ptr2;
    }
    ptr2 += m->size + 1;
  }
  return -1;
}
/*------------------------------------------------------------------------------*/
/**
 * Increase the reference count for a memory chunk.
 *
 * \note No sanity checks are currently made.
 *
 * \param m m A memory block previosly declared with MEMB().
 *
 * \param ptr A pointer to the memory chunk for which the reference
 * count should be increased.
 *
 * \return The new reference count.
 */
/*------------------------------------------------------------------------------*/
char
memb_ref(struct memb_blocks *m, char *ptr)
{
  return ++*(ptr - 1);
}
/*------------------------------------------------------------------------------*/




