
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ALIGN_BY 32
#define ALIGN_BY_M1 (ALIGN_BY - 1)
#define ALIGN(_in) (((_in) + ALIGN_BY_M1) & (~ALIGN_BY_M1))

#define LLOG_LEVEL 1
#define LLOGLN(_log_level, _params) \
do { \
  if (_log_level < LLOG_LEVEL) \
  { \
    printf _params ; \
    printf ("\n") ; \
  } \
} while (0)

struct mem_item
{
  unsigned int addr;
  int bytes;
  struct mem_item* next;
  struct mem_item* prev;
};

struct mem_info
{
  unsigned int addr;
  int bytes;
  int flags;
  struct mem_item* free_head;
  struct mem_item* free_tail;
  struct mem_item* used_head;
  struct mem_item* used_tail;
  int total_bytes;
};

/*****************************************************************************/
static int
libmem_free_mem_item(struct mem_info* self, struct mem_item* mi)
{
  if (self == 0 || mi == 0)
  {
    return 0;
  }
  if (mi->prev != 0)
  {
    mi->prev->next = mi->next;
  }
  if (mi->next != 0)
  {
    mi->next->prev = mi->prev;
  }
  if (mi == self->free_head)
  {
    self->free_head = mi->next;
  }
  if (mi == self->free_tail)
  {
    self->free_tail = mi->prev;
  }
  if (mi == self->used_head)
  {
    self->used_head = mi->next;
  }
  if (mi == self->used_tail)
  {
    self->used_tail = mi->prev;
  }
  free(mi);
  return 0;
}

/*****************************************************************************/
void*
libmem_init(unsigned int addr, int bytes)
{
  struct mem_info* self;
  struct mem_item* mi;

  self = (struct mem_info*)malloc(sizeof(struct mem_info));
  memset(self, 0, sizeof(struct mem_info));
  self->addr = addr;
  self->bytes = bytes;
  //self->flags = 1;
  mi = (struct mem_item*)malloc(sizeof(struct mem_item));
  memset(mi, 0, sizeof(struct mem_item));
  mi->addr = addr;
  mi->bytes = bytes;
  self->free_head = mi;
  self->free_tail = mi;
  return self;
}

/*****************************************************************************/
void
libmem_deinit(void* aself)
{
  struct mem_info* self;

  self = (struct mem_info*)aself;
  if (self == 0)
  {
    return;
  }
  while (self->free_head != 0)
  {
    libmem_free_mem_item(self, self->free_head);
  }
  while (self->used_head != 0)
  {
    libmem_free_mem_item(self, self->used_head);
  }
  free(self);
}

/****************************************************************************/
static int
libmem_add_used_item(struct mem_info* self, unsigned int addr, int bytes)
{
  struct mem_item* mi;
  struct mem_item* new_mi;
  int added;

  if (self == 0 || addr == 0)
  {
    return 1;
  }
  if (self->used_head == 0)
  {
    /* add first item */
    new_mi = (struct mem_item*)malloc(sizeof(struct mem_item));
    memset(new_mi, 0, sizeof(struct mem_item));
    new_mi->addr = addr;
    new_mi->bytes = bytes;
    self->used_head = new_mi;
    self->used_tail = new_mi;
    return 0;
  }
  added = 0;
  mi = self->used_head;
  while (mi != 0)
  {
    if (mi->addr > addr)
    {
      /* add before */
      new_mi = (struct mem_item*)malloc(sizeof(struct mem_item));
      memset(new_mi, 0, sizeof(struct mem_item));
      new_mi->addr = addr;
      new_mi->bytes = bytes;
      new_mi->prev = mi->prev;
      new_mi->next = mi;
      if (mi->prev != 0)
      {
        mi->prev->next = new_mi;
      }
      mi->prev = new_mi;
      if (self->used_head == mi)
      {
        self->used_head = new_mi;
      }
      added = 1;
      break;
    }
    mi = mi->next;
  }
  if (!added)
  {
    /* add last */
    new_mi = (struct mem_item*)malloc(sizeof(struct mem_item));
    memset(new_mi, 0, sizeof(struct mem_item));
    new_mi->addr = addr;
    new_mi->bytes = bytes;
    self->used_tail->next = new_mi;
    new_mi->prev = self->used_tail;
    self->used_tail = new_mi;
  }
  return 0;
}

/****************************************************************************/
static int
libmem_add_free_item(struct mem_info* self, unsigned int addr, int bytes)
{
  struct mem_item* mi;
  struct mem_item* new_mi;
  int added;

  if (self == 0 || addr == 0)
  {
    return 1;
  }
  if (self->free_head == 0)
  {
    /* add first item */
    new_mi = (struct mem_item*)malloc(sizeof(struct mem_item));
    memset(new_mi, 0, sizeof(struct mem_item));
    new_mi->addr = addr;
    new_mi->bytes = bytes;
    self->free_head = new_mi;
    self->free_tail = new_mi;
    return 0;
  }
  added = 0;
  mi = self->free_head;
  while (mi != 0)
  {
    if (mi->addr > addr)
    {
      if (mi->prev != 0)
      {
        if (mi->prev->addr + mi->prev->bytes == addr)
        {
          /* don't need to add, just make prev bigger */
          mi->prev->bytes += bytes;
          if (mi->prev->addr + mi->prev->bytes == mi->addr)
          {
            /* here we can remove one */
            mi->prev->bytes += mi->bytes;
            libmem_free_mem_item(self, mi);
          }
          return 0;
        }
      }
      if (addr + bytes == mi->addr)
      {
        /* don't need to add here either */
        mi->addr = addr;
        mi->bytes += bytes;
        return 0;
      }
      /* add before */
      new_mi = (struct mem_item*)malloc(sizeof(struct mem_item));
      memset(new_mi, 0, sizeof(struct mem_item));
      new_mi->addr = addr;
      new_mi->bytes = bytes;
      new_mi->prev = mi->prev;
      new_mi->next = mi;
      if (mi->prev != 0)
      {
        mi->prev->next = new_mi;
      }
      mi->prev = new_mi;
      if (self->free_head == mi)
      {
        self->free_head = new_mi;
      }
      added = 1;
      break;
    }
    mi = mi->next;
  }
  if (!added)
  {
    /* add last */
    new_mi = (struct mem_item*)malloc(sizeof(struct mem_item));
    memset(new_mi, 0, sizeof(struct mem_item));
    new_mi->addr = addr;
    new_mi->bytes = bytes;
    self->free_tail->next = new_mi;
    new_mi->prev = self->free_tail;
    self->free_tail = new_mi;
  }
  return 0;
}

/*****************************************************************************/
static int
libmem_print(struct mem_info* self)
{
  struct mem_item* mi;

  LLOGLN(0, ("libmem_print:"));
  LLOGLN(0, ("  used_head %p", self->used_head));
  LLOGLN(0, ("  used_tail %p", self->used_tail));
  mi = self->used_head;
  if (mi != 0)
  {
    LLOGLN(0, ("  used list"));
    while (mi != 0)
    {
      LLOGLN(0, ("    ptr %p prev %p next %p addr 0x%8.8x bytes %d",
             mi, mi->prev, mi->next, mi->addr, mi->bytes));
      mi = mi->next;
    }
  }
  LLOGLN(0, ("  free_head %p", self->free_head));
  LLOGLN(0, ("  free_tail %p", self->free_tail));
  mi = self->free_head;
  if (mi != 0)
  {
    LLOGLN(0, ("  free list"));
    while (mi != 0)
    {
      LLOGLN(0, ("    ptr %p prev %p next %p addr 0x%8.8x bytes %d",
             mi, mi->prev, mi->next, mi->addr, mi->bytes));
      mi = mi->next;
    }
  }
  return 0;
}

/*****************************************************************************/
unsigned int
libmem_alloc(void* obj, int bytes)
{
  struct mem_info* self;
  struct mem_item* mi;
  unsigned int addr;

  if (bytes < 1)
  {
    return 0;
  }
  bytes = ALIGN(bytes);
  self = (struct mem_info*)obj;
  addr = 0;
  mi = self->free_head;
  while (mi != 0)
  {
    if (bytes <= mi->bytes)
    {
      addr = mi->addr;
      mi->bytes -= bytes;
      mi->addr += bytes;
      if (mi->bytes < 1)
      {
        libmem_free_mem_item(self, mi);
      }
      break;
    }
    mi = mi->next;
  }
  if (addr != 0)
  {
    self->total_bytes += bytes;
    libmem_add_used_item(self, addr, bytes);
    if (self->flags & 1)
    {
      libmem_print(self);
    }
  }
  else
  {
    LLOGLN(0, ("libmem_alloc: error"));
  }
  return addr;
}

/*****************************************************************************/
int
libmem_free(void* obj, unsigned int addr)
{
  struct mem_info* self;
  struct mem_item* mi;

  if (addr == 0)
  {
    return 0;
  }
  self = (struct mem_info*)obj;
  mi = self->used_tail;
  while (mi != 0)
  {
    if (mi->addr == addr)
    {
      self->total_bytes -= mi->bytes;
      libmem_add_free_item(self, mi->addr, mi->bytes);
      libmem_free_mem_item(self, mi);
      if (self->flags & 1)
      {
        libmem_print(self);
      }
      return 0;
    }
    mi = mi->prev;
  }
  LLOGLN(0, ("libmem_free: error"));
  return 1;
}

/*****************************************************************************/
int
libmem_set_flags(void* obj, int flags)
{
  struct mem_info* self;

  self = (struct mem_info*)obj;
  self->flags |= flags;
  return 0;
}

/*****************************************************************************/
int
libmem_clear_flags(void* obj, int flags)
{
  struct mem_info* self;

  self = (struct mem_info*)obj;
  self->flags &= ~flags;
  return 0;
}

/*****************************************************************************/
int
libmem_get_alloced_bytes(void* obj)
{
  struct mem_info* self;

  self = (struct mem_info*)obj;
  return self->total_bytes;
}
