memory: further memory tweaking
[platform/upstream/gstreamer.git] / gst / gstmemory.c
1 /* GStreamer
2  * Copyright (C) 2011 Wim Taymans <wim.taymans@gmail.be>
3  *
4  * gstmemory.c: memory block handling
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include "gst_private.h"
25
26 #include "gstmemory.h"
27
28 struct _GstMemoryImpl
29 {
30   GQuark name;
31
32   GstMemoryInfo info;
33 };
34
35 typedef struct
36 {
37   GstMemory mem;
38   gsize slice_size;
39   guint8 *data;
40   GFreeFunc free_func;
41   gsize maxsize;
42   gsize offset;
43   gsize size;
44 } GstMemoryDefault;
45
46 static const GstMemoryImpl *_default_mem_impl;
47 static const GstMemoryImpl *_default_sub_impl;
48
49 static void
50 _default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
51     GstMemory * parent, gsize slice_size, gpointer data,
52     GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
53 {
54   mem->mem.impl = data ? _default_mem_impl : _default_sub_impl;
55   mem->mem.flags = flags;
56   mem->mem.refcount = 1;
57   mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
58   mem->slice_size = slice_size;
59   mem->data = data;
60   mem->free_func = free_func;
61   mem->maxsize = maxsize;
62   mem->offset = offset;
63   mem->size = size;
64 }
65
66 static GstMemoryDefault *
67 _default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
68     GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
69 {
70   GstMemoryDefault *mem;
71   gsize slice_size;
72
73   slice_size = sizeof (GstMemoryDefault);
74
75   mem = g_slice_alloc (slice_size);
76   _default_mem_init (mem, flags, parent, slice_size,
77       data, free_func, maxsize, offset, size);
78
79   return mem;
80 }
81
82 static GstMemoryDefault *
83 _default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
84 {
85   GstMemoryDefault *mem;
86   gsize aoffset, slice_size;
87   guint8 *data;
88
89   /* alloc header and data in one block */
90   slice_size = sizeof (GstMemoryDefault) + maxsize + align;
91
92   mem = g_slice_alloc (slice_size);
93   if (mem == NULL)
94     return NULL;
95
96   data = (guint8 *) mem + sizeof (GstMemoryDefault);
97
98   if ((aoffset = ((guintptr) data & align)))
99     aoffset = align - aoffset;
100
101   _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize + align,
102       aoffset + offset, size);
103
104   return mem;
105 }
106
107 static gsize
108 _default_mem_get_sizes (GstMemoryDefault * mem, gsize * maxsize)
109 {
110   if (maxsize)
111     *maxsize = mem->maxsize;
112
113   return mem->size;
114 }
115
116 static void
117 _default_mem_trim (GstMemoryDefault * mem, gsize offset, gsize size)
118 {
119   g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
120
121   mem->offset += offset;
122   mem->size = size;
123 }
124
125 static gpointer
126 _default_mem_map (GstMemoryDefault * mem, gsize * size, gsize * maxsize,
127     GstMapFlags flags)
128 {
129   if (size)
130     *size = mem->size;
131   if (maxsize)
132     *maxsize = mem->maxsize;
133
134   return mem->data + mem->offset;
135 }
136
137 static gpointer
138 _default_sub_map (GstMemoryDefault * mem, gsize * size, gsize * maxsize,
139     GstMapFlags flags)
140 {
141   guint8 *data;
142
143   data = gst_memory_map (mem->mem.parent, size, maxsize, flags);
144
145   if (size)
146     *size = mem->size;
147   if (maxsize)
148     *maxsize -= mem->offset;
149
150   return data + mem->offset;
151 }
152
153 static gboolean
154 _default_mem_unmap (GstMemoryDefault * mem, gpointer data, gsize size)
155 {
156   mem->size = size;
157   return TRUE;
158 }
159
160 static gboolean
161 _default_sub_unmap (GstMemoryDefault * mem, gpointer data, gsize size)
162 {
163   gboolean res;
164   guint8 *ptr = data;
165
166   mem->size = size;
167
168   res =
169       gst_memory_unmap (mem->mem.parent, ptr - mem->offset, size + mem->offset);
170
171   return res;
172 }
173
174 static void
175 _default_mem_free (GstMemoryDefault * mem)
176 {
177   if (mem->mem.parent)
178     gst_memory_unref (mem->mem.parent);
179
180   if (mem->free_func)
181     mem->free_func (mem->data);
182
183   g_slice_free1 (mem->slice_size, mem);
184 }
185
186 static GstMemoryDefault *
187 _default_mem_copy (GstMemoryDefault * mem, gsize offset, gsize size)
188 {
189   GstMemoryDefault *copy;
190
191   if (size == -1)
192     size = mem->size > offset ? mem->size - offset : 0;
193   copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size);
194   memcpy (copy->data, mem->data, mem->maxsize);
195
196   return copy;
197 }
198
199 static GstMemoryDefault *
200 _default_mem_sub (GstMemoryDefault * mem, gsize offset, gsize size)
201 {
202   GstMemoryDefault *sub;
203   GstMemory *parent;
204
205   /* find the real parent */
206   if ((parent = mem->mem.parent) == NULL)
207     parent = (GstMemory *) mem;
208
209   if (size == -1)
210     size = mem->size - offset;
211
212   sub = _default_mem_new (parent->flags, parent, mem->data, NULL, mem->maxsize,
213       mem->offset + offset, size);
214
215   return sub;
216 }
217
218 static gboolean
219 _default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
220     gsize * offset)
221 {
222   if (offset)
223     *offset = mem1->offset;
224
225   /* and memory is contiguous */
226   return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset;
227 }
228
229 static GstMemory *
230 _fallback_copy (GstMemory * mem, gsize offset, gsize size)
231 {
232   GstMemoryDefault *copy;
233   guint8 *data;
234   gsize msize;
235
236   data = gst_memory_map (mem, &msize, NULL, GST_MAP_READ);
237   if (size == -1)
238     size = msize > offset ? msize - offset : 0;
239   copy = _default_mem_new_block (size, 0, 0, size);
240   memcpy (copy->data, data + offset, size);
241   gst_memory_unmap (mem, data, msize);
242
243   return (GstMemory *) copy;
244 }
245
246 static GstMemory *
247 _fallback_sub (GstMemory * mem, gsize offset, gsize size)
248 {
249   GstMemoryDefault *sub;
250   GstMemory *parent;
251
252   /* find the real parent */
253   parent = mem->parent ? mem->parent : mem;
254
255   sub = _default_mem_new (0, parent, NULL, NULL, size, offset, size);
256
257   return (GstMemory *) sub;
258 }
259
260 static gboolean
261 _fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
262 {
263   return FALSE;
264 }
265
266 void
267 _gst_memory_init (void)
268 {
269   static const GstMemoryInfo _mem_info = {
270     (GstMemoryGetSizesFunction) _default_mem_get_sizes,
271     (GstMemoryTrimFunction) _default_mem_trim,
272     (GstMemoryMapFunction) _default_mem_map,
273     (GstMemoryUnmapFunction) _default_mem_unmap,
274     (GstMemoryFreeFunction) _default_mem_free,
275     (GstMemoryCopyFunction) _default_mem_copy,
276     (GstMemorySubFunction) _default_mem_sub,
277     (GstMemoryIsSpanFunction) _default_mem_is_span
278   };
279   static const GstMemoryInfo _sub_info = {
280     (GstMemoryGetSizesFunction) _default_mem_get_sizes,
281     (GstMemoryTrimFunction) _default_mem_trim,
282     (GstMemoryMapFunction) _default_sub_map,
283     (GstMemoryUnmapFunction) _default_sub_unmap,
284     (GstMemoryFreeFunction) _default_mem_free,
285     NULL,
286     NULL,
287     NULL
288   };
289
290   _default_mem_impl = gst_memory_register ("GstMemoryDefault", &_mem_info);
291   _default_sub_impl = gst_memory_register ("GstMemorySubbuffer", &_sub_info);
292 }
293
294 /**
295  * gst_memory_register:
296  * @name:
297  * @info:
298  *
299  * Returns:
300  */
301 const GstMemoryImpl *
302 gst_memory_register (const gchar * name, const GstMemoryInfo * info)
303 {
304   GstMemoryImpl *impl;
305
306 #define INSTALL_FALLBACK(_t) \
307   if (impl->info._t == NULL) impl->info._t = _fallback_ ##_t;
308
309   g_return_val_if_fail (name != NULL, NULL);
310   g_return_val_if_fail (info != NULL, NULL);
311   g_return_val_if_fail (info->get_sizes != NULL, NULL);
312   g_return_val_if_fail (info->trim != NULL, NULL);
313   g_return_val_if_fail (info->map != NULL, NULL);
314   g_return_val_if_fail (info->unmap != NULL, NULL);
315   g_return_val_if_fail (info->free != NULL, NULL);
316
317   impl = g_slice_new (GstMemoryImpl);
318   impl->name = g_quark_from_string (name);
319   impl->info = *info;
320   INSTALL_FALLBACK (copy);
321   INSTALL_FALLBACK (sub);
322   INSTALL_FALLBACK (is_span);
323
324   GST_DEBUG ("register \"%s\" of size %" G_GSIZE_FORMAT, name);
325
326 #if 0
327   g_static_rw_lock_writer_lock (&lock);
328   g_hash_table_insert (memoryimpl, (gpointer) name, (gpointer) impl);
329   g_static_rw_lock_writer_unlock (&lock);
330 #endif
331 #undef INSTALL_FALLBACK
332
333   return impl;
334 }
335
336 GstMemory *
337 gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
338     GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
339 {
340   GstMemoryDefault *mem;
341
342   mem = _default_mem_new (flags, NULL, data, free_func, maxsize, offset, size);
343
344   return (GstMemory *) mem;
345 }
346
347 GstMemory *
348 gst_memory_new_alloc (gsize maxsize, gsize align)
349 {
350   GstMemoryDefault *mem;
351
352   mem = _default_mem_new_block (maxsize, align, 0, maxsize);
353
354   return (GstMemory *) mem;
355 }
356
357 GstMemory *
358 gst_memory_new_copy (gsize maxsize, gsize align, gpointer data,
359     gsize offset, gsize size)
360 {
361   GstMemoryDefault *mem;
362
363   mem = _default_mem_new_block (maxsize, align, offset, size);
364   memcpy (mem->data, data, maxsize);
365
366   return (GstMemory *) mem;
367 }
368
369 /**
370  * gst_memory_ref:
371  * @mem: a #GstMemory
372  *
373  * Returns: @mem with increased refcount
374  */
375 GstMemory *
376 gst_memory_ref (GstMemory * mem)
377 {
378   g_return_val_if_fail (mem != NULL, NULL);
379
380   g_atomic_int_inc (&mem->refcount);
381
382   return mem;
383 }
384
385 /**
386  * gst_memory_unref:
387  * @mem: a #GstMemory
388  */
389 void
390 gst_memory_unref (GstMemory * mem)
391 {
392   g_return_if_fail (mem != NULL);
393   g_return_if_fail (mem->impl != NULL);
394
395   if (g_atomic_int_dec_and_test (&mem->refcount))
396     mem->impl->info.free (mem);
397 }
398
399 /**
400  * gst_memory_get_sizes:
401  * @mem: a #GstMemory
402  * @maxsize: pointer to maxsize
403  *
404  * Returns: the current size of @mem
405  */
406 gsize
407 gst_memory_get_sizes (GstMemory * mem, gsize * maxsize)
408 {
409   g_return_val_if_fail (mem != NULL, 0);
410
411   return mem->impl->info.get_sizes (mem, maxsize);
412 }
413
414 gpointer
415 gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize,
416     GstMapFlags flags)
417 {
418   g_return_val_if_fail (mem != NULL, NULL);
419
420   return mem->impl->info.map (mem, size, maxsize, flags);
421 }
422
423 gboolean
424 gst_memory_unmap (GstMemory * mem, gpointer data, gsize size)
425 {
426   g_return_val_if_fail (mem != NULL, FALSE);
427
428   return mem->impl->info.unmap (mem, data, size);
429 }
430
431 GstMemory *
432 gst_memory_copy (GstMemory * mem, gsize offset, gsize size)
433 {
434   g_return_val_if_fail (mem != NULL, NULL);
435
436   return mem->impl->info.copy (mem, offset, size);
437 }
438
439 void
440 gst_memory_trim (GstMemory * mem, gsize offset, gsize size)
441 {
442   g_return_if_fail (mem != NULL);
443
444   mem->impl->info.trim (mem, offset, size);
445 }
446
447 GstMemory *
448 gst_memory_sub (GstMemory * mem, gsize offset, gsize size)
449 {
450   g_return_val_if_fail (mem != NULL, NULL);
451
452   return mem->impl->info.sub (mem, offset, size);
453 }
454
455 gboolean
456 gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
457 {
458   g_return_val_if_fail (mem1 != NULL, FALSE);
459   g_return_val_if_fail (mem2 != NULL, FALSE);
460
461   /* need to have the same implementation */
462   if (mem1->impl != mem2->impl)
463     return FALSE;
464
465   /* need to have the same parent */
466   if (mem1->parent == NULL || mem1->parent != mem2->parent)
467     return FALSE;
468
469   /* and memory is contiguous */
470   if (!mem1->impl->info.is_span (mem1, mem2, offset))
471     return FALSE;
472
473   return TRUE;
474 }