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