1/*
2  tre-mem.c - TRE memory allocator
3
4  This software is released under a BSD-style license.
5  See the file LICENSE for details and copyright.
6
7*/
8
9/*
10  This memory allocator is for allocating small memory blocks efficiently
11  in terms of memory overhead and execution speed.  The allocated blocks
12  cannot be freed individually, only all at once.  There can be multiple
13  allocators, though.
14*/
15
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif /* HAVE_CONFIG_H */
19#include <stdlib.h>
20#include <string.h>
21
22#include "tre-internal.h"
23#include "tre-mem.h"
24#include "xmalloc.h"
25
26
27/* Returns a new memory allocator or NULL if out of memory. */
28tre_mem_t
29tre_mem_new_impl(int provided, void *provided_block)
30{
31  tre_mem_t mem;
32  if (provided)
33    {
34      mem = provided_block;
35      memset(mem, 0, sizeof(*mem));
36    }
37  else
38    mem = xcalloc(1, sizeof(*mem));
39  if (mem == NULL)
40    return NULL;
41  return mem;
42}
43
44
45/* Frees the memory allocator and all memory allocated with it. */
46void
47tre_mem_destroy(tre_mem_t mem)
48{
49  tre_list_t *tmp, *l = mem->blocks;
50
51  while (l != NULL)
52    {
53      xfree(l->data);
54      tmp = l->next;
55      xfree(l);
56      l = tmp;
57    }
58  xfree(mem);
59}
60
61
62/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
63   allocated block or NULL if an underlying malloc() failed. */
64void *
65tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
66		   int zero, size_t size)
67{
68  void *ptr;
69
70  if (mem->failed)
71    {
72      DPRINT(("tre_mem_alloc: oops, called after failure?!\n"));
73      return NULL;
74    }
75
76#ifdef MALLOC_DEBUGGING
77  if (!provided)
78    {
79      ptr = xmalloc(1);
80      if (ptr == NULL)
81	{
82	  DPRINT(("tre_mem_alloc: xmalloc forced failure\n"));
83	  mem->failed = 1;
84	  return NULL;
85	}
86      xfree(ptr);
87    }
88#endif /* MALLOC_DEBUGGING */
89
90  if (mem->n < size)
91    {
92      /* We need more memory than is available in the current block.
93	 Allocate a new block. */
94      tre_list_t *l;
95      if (provided)
96	{
97	  DPRINT(("tre_mem_alloc: using provided block\n"));
98	  if (provided_block == NULL)
99	    {
100	      DPRINT(("tre_mem_alloc: provided block was NULL\n"));
101	      mem->failed = 1;
102	      return NULL;
103	    }
104	  mem->ptr = provided_block;
105	  mem->n = TRE_MEM_BLOCK_SIZE;
106	}
107      else
108	{
109	  int block_size;
110	  if (size * 8 > TRE_MEM_BLOCK_SIZE)
111	    block_size = size * 8;
112	  else
113	    block_size = TRE_MEM_BLOCK_SIZE;
114	  DPRINT(("tre_mem_alloc: allocating new %d byte block\n",
115		  block_size));
116	  l = xmalloc(sizeof(*l));
117	  if (l == NULL)
118	    {
119	      mem->failed = 1;
120	      return NULL;
121	    }
122	  l->data = xmalloc(block_size);
123	  if (l->data == NULL)
124	    {
125	      xfree(l);
126	      mem->failed = 1;
127	      return NULL;
128	    }
129	  l->next = NULL;
130	  if (mem->current != NULL)
131	    mem->current->next = l;
132	  if (mem->blocks == NULL)
133	    mem->blocks = l;
134	  mem->current = l;
135	  mem->ptr = l->data;
136	  mem->n = block_size;
137	}
138    }
139
140  /* Make sure the next pointer will be aligned. */
141  size += ALIGN(mem->ptr + size, long);
142
143  /* Allocate from current block. */
144  ptr = mem->ptr;
145  mem->ptr += size;
146  mem->n -= size;
147
148  /* Set to zero if needed. */
149  if (zero)
150    memset(ptr, 0, size);
151
152  return ptr;
153}
154
155/* EOF */
156