1 /* This file is part of GEGL
2 *
3 * GEGL is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
7 *
8 * GEGL is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright 2019 Ell
17 */
18
19 #include "config.h"
20
21 #include <string.h>
22
23 #include <glib-object.h>
24
25 #include "gegl-memory-private.h"
26 #include "gegl-scratch.h"
27 #include "gegl-scratch-private.h"
28
29
30 #define GEGL_SCRATCH_MAX_BLOCK_SIZE (1 << 20)
31 #define GEGL_SCRATCH_BLOCK_DATA_OFFSET GEGL_ALIGN (sizeof (GeglScratchBlock))
32
33
34 G_STATIC_ASSERT (GEGL_ALIGNMENT <= G_MAXUINT8);
35
36
37 /* private types */
38
39 typedef struct _GeglScratchBlock GeglScratchBlock;
40 typedef struct _GeglScratchContext GeglScratchContext;
41
42 struct _GeglScratchBlock
43 {
44 GeglScratchContext *context;
45 gsize size;
46 guint8 offset;
47 };
48
49 struct _GeglScratchContext
50 {
51 GeglScratchBlock **blocks;
52 gint n_blocks;
53 gint n_available_blocks;
54 };
55
56
57 /* local function prototypes */
58
59 static GeglScratchContext * gegl_scratch_context_new (void);
60 static void gegl_scratch_context_free (GeglScratchContext *context);
61
62 static GeglScratchBlock * gegl_scratch_block_new (GeglScratchContext *context,
63 gsize size);
64 static void gegl_scratch_block_free (GeglScratchBlock *block);
65
66 static inline gpointer gegl_scratch_block_to_data (GeglScratchBlock *block);
67 static inline GeglScratchBlock * gegl_scratch_block_from_data (gpointer data);
68
69
70 /* local variables */
71
72 static GPrivate gegl_scratch_context = G_PRIVATE_INIT (
73 (GDestroyNotify) gegl_scratch_context_free);
74 static const GeglScratchContext void_context;
75 static volatile guintptr gegl_scratch_total;
76
77
78 /* private functions */
79
80 GeglScratchContext *
gegl_scratch_context_new(void)81 gegl_scratch_context_new (void)
82 {
83 return g_slice_new0 (GeglScratchContext);
84 }
85
86 void
gegl_scratch_context_free(GeglScratchContext * context)87 gegl_scratch_context_free (GeglScratchContext *context)
88 {
89 gint i;
90
91 for (i = 0; i < context->n_available_blocks; i++)
92 gegl_scratch_block_free (context->blocks[i]);
93
94 g_free (context->blocks);
95
96 g_slice_free (GeglScratchContext, context);
97 }
98
99 GeglScratchBlock *
gegl_scratch_block_new(GeglScratchContext * context,gsize size)100 gegl_scratch_block_new (GeglScratchContext *context,
101 gsize size)
102 {
103 GeglScratchBlock *block;
104 gint offset;
105
106 g_atomic_pointer_add (&gegl_scratch_total, +size);
107
108 block = g_malloc ((GEGL_ALIGNMENT - 1) +
109 GEGL_SCRATCH_BLOCK_DATA_OFFSET +
110 size);
111
112 offset = GEGL_ALIGN ((guintptr) block) - (guintptr) block;
113
114 block = (GeglScratchBlock *) ((guint8 *) block + offset);
115
116 block->context = context;
117 block->size = size;
118 block->offset = offset;
119
120 return block;
121 }
122
123 void
gegl_scratch_block_free(GeglScratchBlock * block)124 gegl_scratch_block_free (GeglScratchBlock *block)
125 {
126 g_atomic_pointer_add (&gegl_scratch_total, -block->size);
127
128 g_free ((guint8 *) block - block->offset);
129 }
130
131 static inline gpointer
gegl_scratch_block_to_data(GeglScratchBlock * block)132 gegl_scratch_block_to_data (GeglScratchBlock *block)
133 {
134 return (guint8 *) block + GEGL_SCRATCH_BLOCK_DATA_OFFSET;
135 }
136
137 static inline GeglScratchBlock *
gegl_scratch_block_from_data(gpointer data)138 gegl_scratch_block_from_data (gpointer data)
139 {
140 return (GeglScratchBlock *) ((guint8 *) data -
141 GEGL_SCRATCH_BLOCK_DATA_OFFSET);
142 }
143
144
145 /* public functions */
146
147 gpointer
gegl_scratch_alloc(gsize size)148 gegl_scratch_alloc (gsize size)
149 {
150 GeglScratchContext *context;
151 GeglScratchBlock *block;
152
153 if (G_UNLIKELY (size > GEGL_SCRATCH_MAX_BLOCK_SIZE))
154 {
155 block = gegl_scratch_block_new ((GeglScratchContext *) &void_context,
156 size);
157
158 return gegl_scratch_block_to_data (block);
159 }
160
161 context = g_private_get (&gegl_scratch_context);
162
163 if (G_UNLIKELY (! context))
164 {
165 context = gegl_scratch_context_new ();
166
167 g_private_set (&gegl_scratch_context, context);
168 }
169
170 if (G_LIKELY (context->n_available_blocks))
171 {
172 block = context->blocks[--context->n_available_blocks];
173
174 if (G_LIKELY (size <= block->size))
175 return gegl_scratch_block_to_data (block);
176
177 gegl_scratch_block_free (block);
178 }
179
180 block = gegl_scratch_block_new (context, size);
181
182 return gegl_scratch_block_to_data (block);
183 }
184
185 gpointer
gegl_scratch_alloc0(gsize size)186 gegl_scratch_alloc0 (gsize size)
187 {
188 gpointer ptr;
189
190 ptr = gegl_scratch_alloc (size);
191
192 memset (ptr, 0, size);
193
194 return ptr;
195 }
196
197 void
gegl_scratch_free(gpointer ptr)198 gegl_scratch_free (gpointer ptr)
199 {
200 GeglScratchContext *context;
201 GeglScratchBlock *block;
202
203 context = g_private_get (&gegl_scratch_context);
204 block = gegl_scratch_block_from_data (ptr);
205
206 if (G_UNLIKELY (block->context != context))
207 {
208 gegl_scratch_block_free (block);
209
210 return;
211 }
212
213 if (G_UNLIKELY (context->n_available_blocks == context->n_blocks))
214 {
215 context->n_blocks = MAX (2 * context->n_blocks, 1);
216 context->blocks = g_renew (GeglScratchBlock *, context->blocks,
217 context->n_blocks);
218 }
219
220 context->blocks[context->n_available_blocks++] = block;
221 }
222
223
224 /* public functions (stats) */
225
226 guint64
gegl_scratch_get_total(void)227 gegl_scratch_get_total (void)
228 {
229 return gegl_scratch_total;
230 }
231