1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* GMime
3 * Copyright (C) 2000-2009 Jeffrey Stedfast
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
19 */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "memchunk.h"
30
31
32 /* in glib2, GSearchFunc was combined with GCompareFunc */
33 typedef GCompareFunc GSearchFunc;
34
35 typedef struct _MemChunkFreeNode {
36 struct _MemChunkFreeNode *next;
37 unsigned int atoms;
38 } MemChunkFreeNode;
39
40 typedef struct _MemChunkNodeInfo {
41 struct _MemChunkNodeInfo *next;
42 unsigned char *block;
43 size_t blocksize;
44 size_t atoms;
45 } MemChunkNodeInfo;
46
47 struct _MemChunk {
48 size_t atomsize;
49 size_t atomcount;
50 size_t blocksize;
51 GPtrArray *blocks;
52 gboolean autoclean;
53 MemChunkFreeNode *free;
54 };
55
56
57 static int
tree_compare(MemChunkNodeInfo * a,MemChunkNodeInfo * b)58 tree_compare (MemChunkNodeInfo *a, MemChunkNodeInfo *b)
59 {
60 if (a->block > b->block)
61 return 1;
62 else if (a->block < b->block)
63 return -1;
64 else
65 return 0;
66 }
67
68 static int
tree_search(MemChunkNodeInfo * info,unsigned char * mem)69 tree_search (MemChunkNodeInfo *info, unsigned char *mem)
70 {
71 if (info->block <= mem) {
72 if (mem < info->block + info->blocksize)
73 return 0;
74
75 return 1;
76 }
77
78 return -1;
79 }
80
81 MemChunk *
memchunk_new(size_t atomsize,size_t atomcount,gboolean autoclean)82 memchunk_new (size_t atomsize, size_t atomcount, gboolean autoclean)
83 {
84 MemChunk *chunk;
85
86 /* each atom has to be at least the size of a MemChunkFreeNode
87 for this to work */
88 atomsize = MAX (atomsize, sizeof (MemChunkFreeNode));
89
90 chunk = g_malloc (sizeof (MemChunk));
91 chunk->atomsize = atomsize;
92 chunk->atomcount = atomcount;
93 chunk->blocksize = atomsize * atomcount;
94 chunk->autoclean = autoclean;
95 chunk->blocks = g_ptr_array_new ();
96 chunk->free = NULL;
97
98 return chunk;
99 }
100
101
102 void *
memchunk_alloc(MemChunk * memchunk)103 memchunk_alloc (MemChunk *memchunk)
104 {
105 MemChunkFreeNode *node;
106 char *block;
107
108 if (memchunk->free) {
109 block = (char *) (node = memchunk->free);
110 node->atoms--;
111 if (node->atoms > 0)
112 return (void *) (block + (node->atoms * memchunk->atomsize));
113
114 memchunk->free = node->next;
115
116 return (void *) block;
117 } else {
118 block = g_malloc (memchunk->blocksize);
119 g_ptr_array_add (memchunk->blocks, block);
120 node = (MemChunkFreeNode *) (block + memchunk->atomsize);
121 node->next = NULL;
122 node->atoms = memchunk->atomcount - 1;
123 memchunk->free = node;
124
125 return (void *) block;
126 }
127 }
128
129
130 void *
memchunk_alloc0(MemChunk * memchunk)131 memchunk_alloc0 (MemChunk *memchunk)
132 {
133 void *mem;
134
135 mem = memchunk_alloc (memchunk);
136 memset (mem, 0, memchunk->atomsize);
137
138 return mem;
139 }
140
141
142 void
memchunk_free(MemChunk * memchunk,void * mem)143 memchunk_free (MemChunk *memchunk, void *mem)
144 {
145 MemChunkFreeNode *node;
146
147 node = (MemChunkFreeNode *) mem;
148 node->next = memchunk->free;
149 node->atoms = 1;
150 memchunk->free = node;
151
152 /* yuck, this'll be slow... */
153 if (memchunk->autoclean)
154 memchunk_clean (memchunk);
155 }
156
157
158 void
memchunk_reset(MemChunk * memchunk)159 memchunk_reset (MemChunk *memchunk)
160 {
161 MemChunkFreeNode *node, *next = NULL;
162 int i;
163
164 for (i = 0; i < memchunk->blocks->len; i++) {
165 node = memchunk->blocks->pdata[i];
166 node->atoms = memchunk->atomcount;
167 node->next = next;
168 next = node;
169 }
170
171 memchunk->free = next;
172 }
173
174 void
memchunk_clean(MemChunk * memchunk)175 memchunk_clean (MemChunk *memchunk)
176 {
177 MemChunkNodeInfo *info, *next = NULL;
178 MemChunkFreeNode *node;
179 GTree *tree;
180 int i;
181
182 node = memchunk->free;
183
184 if (memchunk->blocks->len == 0 || node == NULL)
185 return;
186
187 tree = g_tree_new ((GCompareFunc) tree_compare);
188 for (i = 0; i < memchunk->blocks->len; i++) {
189 info = g_alloca (sizeof (MemChunkNodeInfo));
190 info->next = next;
191 info->block = memchunk->blocks->pdata[i];
192 info->blocksize = memchunk->blocksize;
193 info->atoms = 0;
194
195 next = info;
196
197 g_tree_insert (tree, info, info);
198 }
199
200 while (node) {
201 info = g_tree_search (tree, (GSearchFunc) tree_search, node);
202 if (info)
203 info->atoms += node->atoms;
204
205 node = node->next;
206 }
207
208 info = next;
209 while (info) {
210 if (info->atoms == memchunk->atomcount) {
211 MemChunkFreeNode *prev = NULL;
212
213 node = memchunk->free;
214 while (node) {
215 if (tree_search (info, (void *) node) == 0) {
216 /* prune this node from our free-node list */
217 if (prev)
218 prev->next = node->next;
219 else
220 memchunk->free = node->next;
221 } else {
222 prev = node;
223 }
224
225 node = node->next;
226 }
227
228 /* free the block */
229 g_ptr_array_remove_fast (memchunk->blocks, info->block);
230 g_free (info->block);
231 }
232
233 info = info->next;
234 }
235
236 g_tree_destroy (tree);
237 }
238
239 void
memchunk_destroy(MemChunk * memchunk)240 memchunk_destroy (MemChunk *memchunk)
241 {
242 int i;
243
244 for (i = 0; i < memchunk->blocks->len; i++)
245 g_free (memchunk->blocks->pdata[i]);
246
247 g_ptr_array_free (memchunk->blocks, TRUE);
248
249 g_free (memchunk);
250 }
251