summaryrefslogblamecommitdiffstats
path: root/src/mem.c
blob: d37d472003baf6104dbce97ed7685124354d6f09 (plain) (tree)
1
2
3
4
5
6
7
8
                                                                  


                                  
  

                                                              
  


                                                                     
  



















                                                                        







                                                         
                   
                   































                                                              
               





















                                                                       
           






















                                            


























































                                                             



                                         
                 


                                                                          
                                           






























                                                                            
                                                                   
 
                                              
  




                         

                     


                                                              


                                                               
                                                         

                             

                 
                     






















                                              
                                                             















                                                                       
              








































                                                                      
/*	$calcurse: mem.c,v 1.5 2009/07/12 17:48:13 culot Exp $	*/

/*
 * Calcurse - text-based organizer
 *
 * Copyright (c) 2008-2009 Frederic Culot <frederic@culot.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the
 *        following disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the
 *        following disclaimer in the documentation and/or other
 *        materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Send your feedback or comments to : calcurse@culot.org
 * Calcurse home page : http://culot.org/calcurse
 *
 */

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

#include "i18n.h"
#include "utils.h"
#include "mem.h"

enum {
  BLK_STATE,
  BLK_SIZE,
  BLK_ID,
  EXTRA_SPACE_START
};

#define EXTRA_SPACE_END    1
#define EXTRA_SPACE        EXTRA_SPACE_START + EXTRA_SPACE_END

#define MAGIC_ALLOC        0xda
#define MAGIC_FREE         0xdf

struct mem_blk_s {
  unsigned id, size;
  const char *pos;
  struct mem_blk_s *next;
};

typedef struct {
  unsigned ncall, nalloc, nfree;
  struct mem_blk_s *blk;
} mem_stats_t;

static mem_stats_t mstats;


static unsigned
stats_add_blk (size_t size, const char *pos)
{
  struct mem_blk_s *o, **i;

  o = malloc (sizeof (struct mem_blk_s));
  EXIT_IF (o == 0, _("could not allocate memory to store block info"));

  mstats.ncall++;
  
  o->pos = pos;
  o->size = (unsigned)size;
  o->next = 0;
  
  i = &mstats.blk;
  for (i = &mstats.blk; *i; i = &(*i)->next)
    ;
  o->id = mstats.ncall;
  *i = o;

  return o->id;
}

static void
stats_del_blk (unsigned id)
{
  struct mem_blk_s *o, **i;

  EXIT_IF (id < 0, _("Incorrect block id"));

  i = &mstats.blk;
  for (o = mstats.blk; o; o = o->next)
    {
      if (o->id == id)
        {
          *i = o->next;
          free (o);
          return;
        }
      i = &o->next;
    }

  EXIT (_("Block not found"));
  /* NOTREACHED */  
}

void *
xmalloc (size_t size)
{
  void *p;
  
  EXIT_IF (size == 0, _("xmalloc: zero size"));
  p = malloc (size);
  EXIT_IF (p == 0, _("xmalloc: out of memory"));

  return p;
}

void *
xcalloc (size_t nmemb, size_t size)
{
  void *p;

  EXIT_IF (nmemb == 0 || size == 0, _("xcalloc: zero size"));
  EXIT_IF (SIZE_MAX / nmemb < size, _("xcalloc: overflow"));
  p = calloc (nmemb, size);
  EXIT_IF (p == 0, _("xcalloc: out of memory"));

  return p;
}

void *
xrealloc (void *ptr, size_t nmemb, size_t size)
{
  void *new_ptr;
  size_t new_size;

  new_size = nmemb * size;
  EXIT_IF (new_size == 0, _("xrealloc: zero size"));
  EXIT_IF (SIZE_MAX / nmemb < size, _("xrealloc: overflow"));
  new_ptr = realloc (ptr, new_size);
  EXIT_IF (new_ptr == 0, _("xrealloc: out of memory"));

  return new_ptr;
}

char *
xstrdup (const char *str)
{
  size_t len;
  char *cp;

  len = strlen (str) + 1;
  cp = xmalloc (len);

  return strncpy (cp, str, len);
}

void
xfree (void *p)
{
  EXIT_IF (p == 0, _("xfree: null pointer"));
  free (p);
}

void *
dbg_malloc (size_t size, const char *pos)
{
  unsigned *buf;
  
   if (size == 0)
    return (void *)0;
  
  size = EXTRA_SPACE + (size + sizeof (unsigned) - 1) / sizeof (unsigned);
  buf = xmalloc (size * sizeof (unsigned));
  
  buf[BLK_STATE] = MAGIC_ALLOC;             /* state of the block */
  buf[BLK_SIZE] = size;                     /* size of the block */
  buf[BLK_ID] = stats_add_blk (size, pos);  /* identify a block by its id */
  buf[size - 1] = buf[BLK_ID];              /* mark at end of block */

  mstats.nalloc += size;

  return (void *)(buf + EXTRA_SPACE_START);
}

void *
dbg_calloc (size_t nmemb, size_t size, const char *pos)
{
  void *buf;
  
  if (!nmemb || !size)
    return (void *)0;
  
  EXIT_IF (nmemb > SIZE_MAX / size, _("overflow at %s"), pos);

  size *= nmemb;
  if ((buf = dbg_malloc (size, pos)) == 0)
    return (void *)0;

  bzero (buf, size);

  return buf;
}

void *
dbg_realloc (void *ptr, size_t nmemb, size_t size, const char *pos)
{
  unsigned *buf, old_size, new_size, cpy_size;
  
  if (ptr == 0)
    return (void *)0;

  new_size = nmemb *size;
  if (new_size == 0)
    return (void *)0;
  
  EXIT_IF (nmemb > SIZE_MAX / size, _("overflow at %s"), pos);
  
  if ((buf = dbg_malloc (new_size, pos)) == 0)
    return (void *)0;

  old_size = *((unsigned *)ptr - EXTRA_SPACE_START + BLK_SIZE);
  cpy_size = (old_size > new_size) ? new_size : old_size;
  bcopy (ptr, buf, cpy_size);
  
  mem_free (ptr);
  
  return (void *)buf;
}

char *
dbg_strdup (const char *s, const char *pos)
{
  size_t size;
  char *buf;
  
  if (s == 0)
    return (char *)0;

  size = strlen (s);
  if ((buf = dbg_malloc (size + 1, pos)) == 0)
    return (char *)0;

  return strncpy (buf, s, size + 1);
}

void
dbg_free (void *ptr, const char *pos)
{
  unsigned *buf, size;

  EXIT_IF (ptr == 0, _("dbg_free: null pointer at %s"), pos);

  buf = (unsigned *)ptr - EXTRA_SPACE_START;
  size = buf[BLK_SIZE];
  
  EXIT_IF (buf[BLK_STATE] == MAGIC_FREE,
           _("block seems already freed at %s"), pos);
  EXIT_IF (buf[BLK_STATE] != MAGIC_ALLOC,
           _("corrupt block header at %s"), pos);
  EXIT_IF (buf[size - 1] != buf[BLK_ID],
           _("corrupt block end at %s, (end = %u, should be %d)"), pos,
           buf[size - 1], buf[BLK_ID]); 

  buf[0] = MAGIC_FREE;

  stats_del_blk (buf[BLK_ID]);
  
  xfree (buf);
  mstats.nfree += size;
}


#ifdef CALCURSE_MEMORY_DEBUG

static void
dump_block_info (struct mem_blk_s *blk)
{
  if (blk == 0)
    return;
  
  printf (_("---==== MEMORY BLOCK ====----------------\n"));
  printf (_("            id: %u\n"), blk->id);    
  printf (_("          size: %u\n"), blk->size);  
  printf (_("  allocated in: %s\n"), blk->pos);
  printf (_("-----------------------------------------\n"));
}

void
mem_stats (void)
{
  printf ("\n");
  printf (_("+------------------------------+\n"));  
  printf (_("| calcurse memory usage report |\n"));
  printf (_("+------------------------------+\n"));
  printf (_("  number of calls: %u\n"), mstats.ncall);
  printf (_(" allocated blocks: %u\n"), mstats.nalloc);
  printf (_("   unfreed blocks: %u\n"), mstats.nalloc - mstats.nfree);
  printf ("\n");

  if (mstats.nfree < mstats.nalloc)
    {
      struct mem_blk_s *blk;

      for (blk = mstats.blk; blk; blk = blk->next)
        dump_block_info (blk);
    }
}

#endif /* CALCURSE_MEMORY_DEBUG */