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