1 /****************************************************************\
2 * *
3 * Efficient Memory Allocation Routines *
4 * *
5 * Guy St.C. Slater.. mailto:guy@ebi.ac.uk *
6 * Copyright (C) 2000-2009. All Rights Reserved. *
7 * *
8 * This source code is distributed under the terms of the *
9 * GNU General Public License, version 3. See the file COPYING *
10 * or http://www.gnu.org/licenses/gpl.txt for details *
11 * *
12 * If you use this code, please keep this notice intact. *
13 * *
14 \****************************************************************/
15
16 #include <string.h>
17 #include "recyclebin.h"
18
19 #ifndef USE_PTHREADS
20 static GTree *global_recycle_bin_tree = NULL;
21 #endif /* USE_PTHREADS */
22
23 #ifndef USE_PTHREADS
RecycleBin_compare(gconstpointer a,gconstpointer b)24 static gint RecycleBin_compare(gconstpointer a,
25 gconstpointer b){
26 return a - b;
27 }
28 #endif /* USE_PTHREADS */
29
RecycleBin_create(gchar * name,gsize node_size,gint nodes_per_chunk)30 RecycleBin *RecycleBin_create(gchar *name, gsize node_size,
31 gint nodes_per_chunk){
32 register RecycleBin *recycle_bin = g_new(RecycleBin, 1);
33 g_assert(name);
34 g_assert(node_size >= sizeof(gpointer));
35 recycle_bin->ref_count = 1;
36 recycle_bin->name = g_strdup(name);
37 recycle_bin->chunk_list = g_ptr_array_new();
38 recycle_bin->chunk_pos = nodes_per_chunk;
39 recycle_bin->nodes_per_chunk = nodes_per_chunk;
40 recycle_bin->node_size = node_size;
41 recycle_bin->count = 0;
42 recycle_bin->recycle = NULL;
43 #ifndef USE_PTHREADS
44 if(!global_recycle_bin_tree)
45 global_recycle_bin_tree = g_tree_new(RecycleBin_compare);
46 g_tree_insert(global_recycle_bin_tree, recycle_bin, recycle_bin);
47 #endif /* USE_PTHREADS */
48 return recycle_bin;
49 }
50
RecycleBin_destroy(RecycleBin * recycle_bin)51 void RecycleBin_destroy(RecycleBin *recycle_bin){
52 register gint i;
53 if(--recycle_bin->ref_count)
54 return;
55 #ifndef USE_PTHREADS
56 g_assert(global_recycle_bin_tree);
57 g_assert(g_tree_lookup(global_recycle_bin_tree, recycle_bin));
58 g_tree_remove(global_recycle_bin_tree, recycle_bin);
59 if(!g_tree_nnodes(global_recycle_bin_tree)){
60 g_tree_destroy(global_recycle_bin_tree);
61 global_recycle_bin_tree = NULL;
62 }
63 #endif /* USE_PTHREADS */
64 for(i = 0; i < recycle_bin->chunk_list->len; i++)
65 g_free(recycle_bin->chunk_list->pdata[i]);
66 g_ptr_array_free(recycle_bin->chunk_list, TRUE);
67 g_free(recycle_bin->name);
68 g_free(recycle_bin);
69 return;
70 }
71
RecycleBin_share(RecycleBin * recycle_bin)72 RecycleBin *RecycleBin_share(RecycleBin *recycle_bin){
73 g_assert(recycle_bin);
74 recycle_bin->ref_count++;
75 return recycle_bin;
76 }
77
RecycleBin_memory_usage(RecycleBin * recycle_bin)78 gsize RecycleBin_memory_usage(RecycleBin *recycle_bin){
79 g_assert(recycle_bin);
80 return recycle_bin->node_size
81 * recycle_bin->nodes_per_chunk
82 * recycle_bin->chunk_list->len;
83 }
84
RecycleBin_alloc(RecycleBin * recycle_bin)85 gpointer RecycleBin_alloc(RecycleBin *recycle_bin){
86 register RecycleBin_Node *node;
87 register gchar *chunk;
88 g_assert(recycle_bin);
89 if(recycle_bin->recycle){
90 node = (gpointer)recycle_bin->recycle;
91 recycle_bin->recycle = node->next;
92 } else {
93 if(recycle_bin->chunk_pos == recycle_bin->nodes_per_chunk){
94 chunk = g_malloc(recycle_bin->nodes_per_chunk
95 * recycle_bin->node_size);
96 g_ptr_array_add(recycle_bin->chunk_list, chunk);
97 recycle_bin->chunk_pos = 1;
98 node = (RecycleBin_Node*)chunk;
99 } else {
100 chunk = recycle_bin->chunk_list->pdata
101 [recycle_bin->chunk_list->len-1];
102 node = (RecycleBin_Node*) (chunk
103 + (recycle_bin->node_size
104 * recycle_bin->chunk_pos++));
105 }
106 }
107 recycle_bin->count++;
108 return (gpointer)node;
109 }
110
RecycleBin_alloc_blank(RecycleBin * recycle_bin)111 gpointer RecycleBin_alloc_blank(RecycleBin *recycle_bin){
112 register gpointer data = RecycleBin_alloc(recycle_bin);
113 memset(data, 0, recycle_bin->node_size);
114 return data;
115 }
116
RecycleBin_recycle(RecycleBin * recycle_bin,gpointer data)117 void RecycleBin_recycle(RecycleBin *recycle_bin, gpointer data){
118 register RecycleBin_Node *node = data;
119 g_assert(node != recycle_bin->recycle);
120 node->next = recycle_bin->recycle;
121 recycle_bin->recycle = node;
122 recycle_bin->count--;
123 return;
124 }
125
126 #ifndef USE_PTHREADS
RecycleBin_profile_traverse(gpointer key,gpointer value,gpointer data)127 static gint RecycleBin_profile_traverse(gpointer key,
128 gpointer value,
129 gpointer data){
130 register RecycleBin *recycle_bin = value;
131 g_assert(recycle_bin);
132 g_message(" RecycleBin [%s] %d Mb (%d items)",
133 recycle_bin->name,
134 (gint)(RecycleBin_memory_usage(recycle_bin)>>20),
135 recycle_bin->count);
136 return FALSE;
137 }
138 #endif /* USE_PTHREADS */
139
RecycleBin_profile(void)140 void RecycleBin_profile(void){
141 g_message("BEGIN RecycleBin profile");
142 #ifdef USE_PTHREADS
143 g_message("multi-threaded RecycleBin_profile() not implemented");
144 #else /* USE_PTHREADS */
145 if(global_recycle_bin_tree)
146 g_tree_traverse(global_recycle_bin_tree,
147 RecycleBin_profile_traverse, G_IN_ORDER, NULL);
148 else
149 g_message("no active RecycleBins");
150 #endif /* USE_PTHREADS */
151 g_message("END RecycleBin profile");
152 return;
153 }
154
155 /* FIXME: could use Trash Stacks when migrating to glib-2 */
156
157