memory: more fixes
[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, GstMemory * parent,
51     gsize slice_size, gpointer data, GFreeFunc free_func,
52     gsize maxsize, gsize offset, gsize size)
53 {
54   mem->mem.impl = data ? _default_mem_impl : _default_sub_impl;
55   mem->mem.flags = 0;
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 (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, 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, 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)
188 {
189   GstMemoryDefault *copy;
190
191   copy = _default_mem_new_block (mem->maxsize, 0, mem->offset, mem->size);
192   memcpy (copy->data + copy->offset, mem->data, mem->maxsize);
193
194   return copy;
195 }
196
197 static void
198 _default_mem_extract (GstMemoryDefault * mem, gsize offset, gpointer dest,
199     gsize size)
200 {
201   g_return_if_fail (size + mem->offset + offset > mem->maxsize);
202
203   memcpy (dest, mem->data + mem->offset + offset, size);
204 }
205
206 static GstMemoryDefault *
207 _default_mem_sub (GstMemoryDefault * mem, gsize offset, gsize size)
208 {
209   GstMemoryDefault *sub;
210   GstMemory *parent;
211
212   /* find the real parent */
213   if ((parent = mem->mem.parent) == NULL)
214     parent = (GstMemory *) mem;
215
216   sub = _default_mem_new (parent, mem->data, NULL, mem->maxsize,
217       mem->offset + offset, size);
218
219   return sub;
220 }
221
222 static gboolean
223 _default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
224     gsize * offset)
225 {
226   if (offset)
227     *offset = mem1->offset;
228
229   /* and memory is contiguous */
230   return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset;
231 }
232
233 static void
234 _fallback_extract (GstMemory * mem, gsize offset, gpointer dest, gsize size)
235 {
236   guint8 *data;
237   gsize msize;
238
239   data = gst_memory_map (mem, &msize, NULL, GST_MAP_READ);
240   memcpy (dest, data + offset, size);
241   gst_memory_unmap (mem, data, msize);
242 }
243
244 static GstMemory *
245 _fallback_copy (GstMemory * mem)
246 {
247   GstMemoryDefault *copy;
248   gpointer data;
249   gsize size;
250
251   data = gst_memory_map (mem, &size, NULL, GST_MAP_READ);
252   copy = _default_mem_new_block (size, 0, 0, size);
253   memcpy (copy->data, data, size);
254   gst_memory_unmap (mem, data, size);
255
256   return (GstMemory *) copy;
257 }
258
259 static GstMemory *
260 _fallback_sub (GstMemory * mem, gsize offset, gsize size)
261 {
262   GstMemoryDefault *sub;
263   GstMemory *parent;
264
265   /* find the real parent */
266   parent = mem->parent ? mem->parent : mem;
267
268   sub = _default_mem_new (parent, NULL, NULL, size, offset, size);
269
270   return (GstMemory *) sub;
271 }
272
273 static gboolean
274 _fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
275 {
276   return FALSE;
277 }
278
279 const GstMemoryImpl *
280 gst_memory_register (const gchar * name, const GstMemoryInfo * info)
281 {
282   GstMemoryImpl *impl;
283
284 #define INSTALL_FALLBACK(_t) \
285   if (impl->info._t == NULL) impl->info._t = _fallback_ ##_t;
286
287   g_return_val_if_fail (name != NULL, NULL);
288   g_return_val_if_fail (info != NULL, NULL);
289   g_return_val_if_fail (info->get_sizes != NULL, NULL);
290   g_return_val_if_fail (info->trim != NULL, NULL);
291   g_return_val_if_fail (info->map != NULL, NULL);
292   g_return_val_if_fail (info->unmap != NULL, NULL);
293   g_return_val_if_fail (info->free != NULL, NULL);
294
295   impl = g_slice_new (GstMemoryImpl);
296   impl->name = g_quark_from_string (name);
297   impl->info = *info;
298   INSTALL_FALLBACK (copy);
299   INSTALL_FALLBACK (extract);
300   INSTALL_FALLBACK (sub);
301   INSTALL_FALLBACK (is_span);
302
303   GST_DEBUG ("register \"%s\" of size %" G_GSIZE_FORMAT, name);
304
305 #if 0
306   g_static_rw_lock_writer_lock (&lock);
307   g_hash_table_insert (memoryimpl, (gpointer) name, (gpointer) impl);
308   g_static_rw_lock_writer_unlock (&lock);
309 #endif
310 #undef INSTALL_FALLBACK
311
312   return impl;
313 }
314
315 void
316 _gst_memory_init (void)
317 {
318   static const GstMemoryInfo _mem_info = {
319     (GstMemoryGetSizesFunction) _default_mem_get_sizes,
320     (GstMemoryTrimFunction) _default_mem_trim,
321     (GstMemoryMapFunction) _default_mem_map,
322     (GstMemoryUnmapFunction) _default_mem_unmap,
323     (GstMemoryFreeFunction) _default_mem_free,
324     (GstMemoryCopyFunction) _default_mem_copy,
325     (GstMemoryExtractFunction) _default_mem_extract,
326     (GstMemorySubFunction) _default_mem_sub,
327     (GstMemoryIsSpanFunction) _default_mem_is_span
328   };
329   static const GstMemoryInfo _sub_info = {
330     (GstMemoryGetSizesFunction) _default_mem_get_sizes,
331     (GstMemoryTrimFunction) _default_mem_trim,
332     (GstMemoryMapFunction) _default_sub_map,
333     (GstMemoryUnmapFunction) _default_sub_unmap,
334     (GstMemoryFreeFunction) _default_mem_free,
335     NULL,
336     NULL,
337     NULL,
338     NULL
339   };
340
341   _default_mem_impl = gst_memory_register ("GstMemoryDefault", &_mem_info);
342   _default_sub_impl = gst_memory_register ("GstMemorySubbuffer", &_sub_info);
343 }
344
345 GstMemory *
346 gst_memory_ref (GstMemory * mem)
347 {
348   g_return_val_if_fail (mem != NULL, NULL);
349
350   g_atomic_int_inc (&mem->refcount);
351
352   return mem;
353 }
354
355 void
356 gst_memory_unref (GstMemory * mem)
357 {
358   g_return_if_fail (mem != NULL);
359
360   if (g_atomic_int_dec_and_test (&mem->refcount))
361     mem->impl->info.free (mem);
362 }
363
364 gsize
365 gst_memory_get_sizes (GstMemory * mem, gsize * maxsize)
366 {
367   g_return_val_if_fail (mem != NULL, 0);
368
369   return mem->impl->info.get_sizes (mem, maxsize);
370 }
371
372 gpointer
373 gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize,
374     GstMapFlags flags)
375 {
376   g_return_val_if_fail (mem != NULL, NULL);
377
378   return mem->impl->info.map (mem, size, maxsize, flags);
379 }
380
381 gboolean
382 gst_memory_unmap (GstMemory * mem, gpointer data, gsize size)
383 {
384   g_return_val_if_fail (mem != NULL, FALSE);
385
386   return mem->impl->info.unmap (mem, data, size);
387 }
388
389 GstMemory *
390 gst_memory_copy (GstMemory * mem)
391 {
392   g_return_val_if_fail (mem != NULL, NULL);
393
394   return mem->impl->info.copy (mem);
395 }
396
397 void
398 gst_memory_extract (GstMemory * mem, gsize offset, gpointer dest, gsize size)
399 {
400   g_return_if_fail (mem != NULL);
401   g_return_if_fail (dest != NULL);
402
403   return mem->impl->info.extract (mem, offset, dest, size);
404 }
405
406 void
407 gst_memory_trim (GstMemory * mem, gsize offset, gsize size)
408 {
409   g_return_if_fail (mem != NULL);
410
411   mem->impl->info.trim (mem, offset, size);
412 }
413
414 GstMemory *
415 gst_memory_sub (GstMemory * mem, gsize offset, gsize size)
416 {
417   g_return_val_if_fail (mem != NULL, NULL);
418
419   return mem->impl->info.sub (mem, offset, size);
420 }
421
422 gboolean
423 gst_memory_is_span (GstMemory ** mem1, gsize len1, GstMemory ** mem2,
424     gsize len2, GstMemory ** parent, gsize * offset)
425 {
426   GstMemory *m1, *m2, **arr;
427   gsize len, i;
428   guint count;
429   gboolean have_offset = FALSE;
430
431   g_return_val_if_fail (mem1 != NULL, FALSE);
432   g_return_val_if_fail (mem2 != NULL, FALSE);
433
434   arr = mem1;
435   len = len1;
436   m1 = m2 = NULL;
437
438   for (count = 0; count < 2; count++) {
439     gsize offs;
440
441     for (i = 0; i < len; i++) {
442       if (m2)
443         m1 = m2;
444       m2 = arr[i];
445
446       if (m1 && m2) {
447         /* need to have the same implementation */
448         if (m1->impl != m2->impl)
449           return FALSE;
450
451         /* need to have the same parent */
452         if (m1->parent == NULL || m1->parent != m2->parent)
453           return FALSE;
454
455         /* and memory is contiguous */
456         if (!m1->impl->info.is_span (m1, m2, &offs))
457           return FALSE;
458
459         if (!have_offset) {
460           *offset = offs;
461           have_offset = TRUE;
462         }
463       }
464     }
465     arr = mem2;
466     len = len2;
467   }
468   if (!have_offset)
469     return FALSE;
470
471   return TRUE;
472 }
473
474 GstMemory *
475 gst_memory_span (GstMemory ** mem1, gsize len1, gsize offset, GstMemory ** mem2,
476     gsize len2, gsize size)
477 {
478   GstMemory *span, **mem, *parent;
479   guint8 *data, *dest;
480   gsize count, ssize, tocopy, len, poffset, i;
481
482   g_return_val_if_fail (mem1 != NULL, NULL);
483   g_return_val_if_fail (mem2 != NULL, NULL);
484
485   if (gst_memory_is_span (mem1, len1, mem2, len2, &parent, &poffset)) {
486     span = gst_memory_sub (parent, offset + poffset, size);
487   } else {
488     GstMemoryDefault *tspan;
489
490     tspan = _default_mem_new_block (size, 0, 0, size);
491     dest = tspan->data;
492
493     mem = mem1;
494     len = len1;
495
496     for (count = 0; count < 2; count++) {
497       for (i = 0; i < len && size > 0; i++) {
498         data = gst_memory_map (mem[i], &ssize, NULL, GST_MAP_READ);
499         tocopy = MIN (ssize, size);
500         if (tocopy > offset) {
501           memcpy (dest, data + offset, tocopy - offset);
502           size -= tocopy;
503           dest += tocopy;
504           offset = 0;
505         } else {
506           offset -= tocopy;
507         }
508         gst_memory_unmap (mem[i], data, ssize);
509       }
510       mem = mem2;
511       len = len2;
512     }
513     span = (GstMemory *) tspan;
514   }
515   return span;
516 }
517
518 GstMemory *
519 gst_memory_new_wrapped (gpointer data, GFreeFunc free_func,
520     gsize maxsize, gsize offset, gsize size)
521 {
522   GstMemoryDefault *mem;
523
524   mem = _default_mem_new (NULL, data, free_func, maxsize, offset, size);
525
526   return (GstMemory *) mem;
527 }
528
529 GstMemory *
530 gst_memory_new_alloc (gsize maxsize, gsize align)
531 {
532   GstMemoryDefault *mem;
533
534   mem = _default_mem_new_block (maxsize, align, 0, 0);
535
536   return (GstMemory *) mem;
537 }
538
539 GstMemory *
540 gst_memory_new_copy (gsize maxsize, gsize align, gpointer data,
541     gsize offset, gsize size)
542 {
543   GstMemoryDefault *mem;
544
545   mem = _default_mem_new_block (maxsize, align, offset, size);
546   memcpy (mem->data, data, maxsize);
547
548   return (GstMemory *) mem;
549 }