]> git.sesse.net Git - vlc/blob - src/misc/block.c
decoder: inline DecoderSignalWait()
[vlc] / src / misc / block.c
1 /*****************************************************************************
2  * block.c: Data blocks management functions
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VLC authors and VideoLAN
5  * Copyright (C) 2007-2009 RĂ©mi Denis-Courmont
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <sys/stat.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36
37 #include <vlc_common.h>
38 #include <vlc_block.h>
39 #include <vlc_fs.h>
40
41 /**
42  * @section Block handling functions.
43  */
44
45 #ifndef NDEBUG
46 static void BlockNoRelease( block_t *b )
47 {
48     fprintf( stderr, "block %p has no release callback! This is a bug!\n", b );
49     abort();
50 }
51
52 static void block_Check (block_t *block)
53 {
54     while (block != NULL)
55     {
56         unsigned char *start = block->p_start;
57         unsigned char *end = block->p_start + block->i_size;
58         unsigned char *bufstart = block->p_buffer;
59         unsigned char *bufend = block->p_buffer + block->i_buffer;
60
61         assert (block->pf_release != BlockNoRelease);
62         assert (start <= end);
63         assert (bufstart <= bufend);
64         assert (bufstart >= start);
65         assert (bufend <= end);
66
67         block = block->p_next;
68     }
69 }
70
71 static void block_Invalidate (block_t *block)
72 {
73     block->p_next = NULL;
74     block_Check (block);
75     block->pf_release = BlockNoRelease;
76 }
77 #else
78 # define block_Check(b) ((void)(b))
79 # define block_Invalidate(b) ((void)(b))
80 #endif
81
82 void block_Init( block_t *restrict b, void *buf, size_t size )
83 {
84     /* Fill all fields to their default */
85     b->p_next = NULL;
86     b->p_buffer = buf;
87     b->i_buffer = size;
88     b->p_start = buf;
89     b->i_size = size;
90     b->i_flags = 0;
91     b->i_nb_samples = 0;
92     b->i_pts =
93     b->i_dts = VLC_TS_INVALID;
94     b->i_length = 0;
95 #ifndef NDEBUG
96     b->pf_release = BlockNoRelease;
97 #endif
98 }
99
100 static void block_generic_Release (block_t *block)
101 {
102     /* That is always true for blocks allocated with block_Alloc(). */
103     assert (block->p_start == (unsigned char *)(block + 1));
104     block_Invalidate (block);
105     free (block);
106 }
107
108 static void BlockMetaCopy( block_t *restrict out, const block_t *in )
109 {
110     out->p_next    = in->p_next;
111     out->i_nb_samples = in->i_nb_samples;
112     out->i_dts     = in->i_dts;
113     out->i_pts     = in->i_pts;
114     out->i_flags   = in->i_flags;
115     out->i_length  = in->i_length;
116 }
117
118 /** Initial memory alignment of data block.
119  * @note This must be a multiple of sizeof(void*) and a power of two.
120  * libavcodec AVX optimizations require at least 32-bytes. */
121 #define BLOCK_ALIGN        32
122
123 /** Initial reserved header and footer size. */
124 #define BLOCK_PADDING      32
125
126 /* Maximum size of reserved footer before shrinking with realloc(). */
127 #define BLOCK_WASTE_SIZE   2048
128
129 block_t *block_Alloc (size_t size)
130 {
131     /* 2 * BLOCK_PADDING: pre + post padding */
132     const size_t alloc = sizeof (block_t) + BLOCK_ALIGN + (2 * BLOCK_PADDING)
133                        + size;
134     if (unlikely(alloc <= size))
135         return NULL;
136
137     block_t *b = malloc (alloc);
138     if (unlikely(b == NULL))
139         return NULL;
140
141     block_Init (b, b + 1, alloc - sizeof (*b));
142     static_assert ((BLOCK_PADDING % BLOCK_ALIGN) == 0,
143                    "BLOCK_PADDING must be a multiple of BLOCK_ALIGN");
144     b->p_buffer += BLOCK_PADDING + BLOCK_ALIGN - 1;
145     b->p_buffer = (void *)(((uintptr_t)b->p_buffer) & ~(BLOCK_ALIGN - 1));
146     b->i_buffer = size;
147     b->pf_release = block_generic_Release;
148     return b;
149 }
150
151 block_t *block_Realloc( block_t *p_block, ssize_t i_prebody, size_t i_body )
152 {
153     size_t requested = i_prebody + i_body;
154
155     block_Check( p_block );
156
157     /* Corner case: empty block requested */
158     if( i_prebody <= 0 && i_body <= (size_t)(-i_prebody) )
159     {
160         block_Release( p_block );
161         return NULL;
162     }
163
164     assert( p_block->p_start <= p_block->p_buffer );
165     assert( p_block->p_start + p_block->i_size
166                                     >= p_block->p_buffer + p_block->i_buffer );
167
168     /* Corner case: the current payload is discarded completely */
169     if( i_prebody <= 0 && p_block->i_buffer <= (size_t)-i_prebody )
170          p_block->i_buffer = 0; /* discard current payload */
171     if( p_block->i_buffer == 0 )
172     {
173         if( requested <= p_block->i_size )
174         {   /* Enough room: recycle buffer */
175             size_t extra = p_block->i_size - requested;
176
177             p_block->p_buffer = p_block->p_start + (extra / 2);
178             p_block->i_buffer = requested;
179             return p_block;
180         }
181         /* Not enough room: allocate a new buffer */
182         block_t *p_rea = block_Alloc( requested );
183         if( p_rea )
184             BlockMetaCopy( p_rea, p_block );
185         block_Release( p_block );
186         return p_rea;
187     }
188
189     /* First, shrink payload */
190
191     /* Pull payload start */
192     if( i_prebody < 0 )
193     {
194         assert( p_block->i_buffer >= (size_t)-i_prebody );
195         p_block->p_buffer -= i_prebody;
196         p_block->i_buffer += i_prebody;
197         i_body += i_prebody;
198         i_prebody = 0;
199     }
200
201     /* Trim payload end */
202     if( p_block->i_buffer > i_body )
203         p_block->i_buffer = i_body;
204
205     uint8_t *p_start = p_block->p_start;
206     uint8_t *p_end = p_start + p_block->i_size;
207
208     /* Second, reallocate the buffer if we lack space. This is done now to
209      * minimize the payload size for memory copy. */
210     assert( i_prebody >= 0 );
211     if( (size_t)(p_block->p_buffer - p_start) < (size_t)i_prebody
212      || (size_t)(p_end - p_block->p_buffer) < i_body )
213     {
214         block_t *p_rea = block_Alloc( requested );
215         if( p_rea )
216         {
217             BlockMetaCopy( p_rea, p_block );
218             p_rea->p_buffer += i_prebody;
219             p_rea->i_buffer -= i_prebody;
220             memcpy( p_rea->p_buffer, p_block->p_buffer, p_block->i_buffer );
221         }
222         block_Release( p_block );
223         if( p_rea == NULL )
224             return NULL;
225         p_block = p_rea;
226     }
227     else
228     /* We have a very large reserved footer now? Release some of it.
229      * XXX it might not preserve the alignment of p_buffer */
230     if( p_end - (p_block->p_buffer + i_body) > BLOCK_WASTE_SIZE )
231     {
232         block_t *p_rea = block_Alloc( requested );
233         if( p_rea )
234         {
235             BlockMetaCopy( p_rea, p_block );
236             p_rea->p_buffer += i_prebody;
237             p_rea->i_buffer -= i_prebody;
238             memcpy( p_rea->p_buffer, p_block->p_buffer, p_block->i_buffer );
239             block_Release( p_block );
240             p_block = p_rea;
241         }
242     }
243
244     /* NOTE: p_start and p_end are corrupted from this point */
245
246     /* Third, expand payload */
247
248     /* Push payload start */
249     if( i_prebody > 0 )
250     {
251         p_block->p_buffer -= i_prebody;
252         p_block->i_buffer += i_prebody;
253         i_body += i_prebody;
254         i_prebody = 0;
255     }
256
257     /* Expand payload to requested size */
258     p_block->i_buffer = i_body;
259
260     return p_block;
261 }
262
263
264 static void block_heap_Release (block_t *block)
265 {
266     block_Invalidate (block);
267     free (block->p_start);
268     free (block);
269 }
270
271 /**
272  * Creates a block from a heap allocation.
273  * This is provided by LibVLC so that manually heap-allocated blocks can safely
274  * be deallocated even after the origin plugin has been unloaded from memory.
275  *
276  * When block_Release() is called, VLC will free() the specified pointer.
277  *
278  * @param ptr base address of the heap allocation (will be free()'d)
279  * @param length bytes length of the heap allocation
280  * @return NULL in case of error (ptr free()'d in that case), or a valid
281  * block_t pointer.
282  */
283 block_t *block_heap_Alloc (void *addr, size_t length)
284 {
285     block_t *block = malloc (sizeof (*block));
286     if (block == NULL)
287     {
288         free (addr);
289         return NULL;
290     }
291
292     block_Init (block, addr, length);
293     block->pf_release = block_heap_Release;
294     return block;
295 }
296
297 #ifdef HAVE_MMAP
298 # include <sys/mman.h>
299
300 static void block_mmap_Release (block_t *block)
301 {
302     block_Invalidate (block);
303     munmap (block->p_start, block->i_size);
304     free (block);
305 }
306
307 /**
308  * Creates a block from a virtual address memory mapping (mmap).
309  * This is provided by LibVLC so that mmap blocks can safely be deallocated
310  * even after the allocating plugin has been unloaded from memory.
311  *
312  * @param addr base address of the mapping (as returned by mmap)
313  * @param length length (bytes) of the mapping (as passed to mmap)
314  * @return NULL if addr is MAP_FAILED, or an error occurred (in the later
315  * case, munmap(addr, length) is invoked before returning).
316  */
317 block_t *block_mmap_Alloc (void *addr, size_t length)
318 {
319     if (addr == MAP_FAILED)
320         return NULL;
321
322     block_t *block = malloc (sizeof (*block));
323     if (block == NULL)
324     {
325         munmap (addr, length);
326         return NULL;
327     }
328
329     block_Init (block, addr, length);
330     block->pf_release = block_mmap_Release;
331     return block;
332 }
333 #else
334 block_t *block_mmap_Alloc (void *addr, size_t length)
335 {
336     (void)addr; (void)length; return NULL;
337 }
338 #endif
339
340 #ifdef HAVE_SYS_SHM_H
341 # include <sys/shm.h>
342
343 typedef struct block_shm_t
344 {
345     block_t     self;
346     void       *base_addr;
347 } block_shm_t;
348
349 static void block_shm_Release (block_t *block)
350 {
351     block_shm_t *p_sys = (block_shm_t *)block;
352
353     shmdt (p_sys->base_addr);
354     free (p_sys);
355 }
356
357 /**
358  * Creates a block from a System V shared memory segment (shmget()).
359  * This is provided by LibVLC so that segments can safely be deallocated
360  * even after the allocating plugin has been unloaded from memory.
361  *
362  * @param addr base address of the segment (as returned by shmat())
363  * @param length length (bytes) of the segment (as passed to shmget())
364  * @return NULL if an error occurred (in that case, shmdt(addr) is invoked
365  * before returning NULL).
366  */
367 block_t *block_shm_Alloc (void *addr, size_t length)
368 {
369     block_shm_t *block = malloc (sizeof (*block));
370     if (unlikely(block == NULL))
371     {
372         shmdt (addr);
373         return NULL;
374     }
375
376     block_Init (&block->self, (uint8_t *)addr, length);
377     block->self.pf_release = block_shm_Release;
378     block->base_addr = addr;
379     return &block->self;
380 }
381 #else
382 block_t *block_shm_Alloc (void *addr, size_t length)
383 {
384     (void) addr; (void) length;
385     abort ();
386 }
387 #endif
388
389
390 #ifdef _WIN32
391 # include <io.h>
392
393 static
394 ssize_t pread (int fd, void *buf, size_t count, off_t offset)
395 {
396     HANDLE handle = (HANDLE)(intptr_t)_get_osfhandle (fd);
397     if (handle == INVALID_HANDLE_VALUE)
398         return -1;
399
400     OVERLAPPED olap; olap.Offset = offset; olap.OffsetHigh = (offset >> 32);
401     DWORD written;
402     /* This braindead API will override the file pointer even if we specify
403      * an explicit read offset... So do not expect this to mix well with
404      * regular read() calls. */
405     if (ReadFile (handle, buf, count, &written, &olap))
406         return written;
407     return -1;
408 }
409 #endif
410
411 /**
412  * Loads a file into a block of memory through a file descriptor.
413  * If possible a private file mapping is created. Otherwise, the file is read
414  * normally. This function is a cancellation point.
415  *
416  * @note On 32-bits platforms,
417  * this function will not work for very large files,
418  * due to memory space constraints.
419  *
420  * @param fd file descriptor to load from
421  * @return a new block with the file content at p_buffer, and file length at
422  * i_buffer (release it with block_Release()), or NULL upon error (see errno).
423  */
424 block_t *block_File (int fd)
425 {
426     size_t length;
427     struct stat st;
428
429     /* First, get the file size */
430     if (fstat (fd, &st))
431         return NULL;
432
433     /* st_size is meaningful for regular files, shared memory and typed memory.
434      * It's also meaning for symlinks, but that's not possible with fstat().
435      * In other cases, it's undefined, and we should really not go further. */
436 #ifndef S_TYPEISSHM
437 # define S_TYPEISSHM( buf ) (0)
438 #endif
439     if (S_ISDIR (st.st_mode))
440     {
441         errno = EISDIR;
442         return NULL;
443     }
444     if (!S_ISREG (st.st_mode) && !S_TYPEISSHM (&st))
445     {
446         errno = ESPIPE;
447         return NULL;
448     }
449
450     /* Prevent an integer overflow in mmap() and malloc() */
451     if ((uintmax_t)st.st_size >= SIZE_MAX)
452     {
453         errno = ENOMEM;
454         return NULL;
455     }
456     length = (size_t)st.st_size;
457
458 #ifdef HAVE_MMAP
459     if (length > 0)
460     {
461         void *addr;
462
463         addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
464         if (addr != MAP_FAILED)
465             return block_mmap_Alloc (addr, length);
466     }
467 #endif
468
469     /* If mmap() is not implemented by the OS _or_ the filesystem... */
470     block_t *block = block_Alloc (length);
471     if (block == NULL)
472         return NULL;
473     block_cleanup_push (block);
474
475     for (size_t i = 0; i < length;)
476     {
477         ssize_t len = pread (fd, block->p_buffer + i, length - i, i);
478         if (len == -1)
479         {
480             block_Release (block);
481             block = NULL;
482             break;
483         }
484         i += len;
485     }
486     vlc_cleanup_pop ();
487     return block;
488 }
489
490 /**
491  * Loads a file into a block of memory from the file path.
492  * See also block_File().
493  */
494 block_t *block_FilePath (const char *path)
495 {
496     int fd = vlc_open (path, O_RDONLY);
497     if (fd == -1)
498         return NULL;
499
500     block_t *block = block_File (fd);
501     close (fd);
502     return block;
503 }