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
7 * Authors: Laurent Aimar <fenrir@videolan.org>
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.
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.
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 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_block.h>
39 static void BlockNoRelease( block_t
*b
)
41 fprintf( stderr
, "block %p has no release callback! This is a bug!\n",
46 static void block_Check (block_t
*block
)
50 unsigned char *start
= block
->p_start
;
51 unsigned char *end
= block
->p_start
+ block
->i_size
;
52 unsigned char *bufstart
= block
->p_buffer
;
53 unsigned char *bufend
= block
->p_buffer
+ block
->i_buffer
;
55 assert (block
->pf_release
!= BlockNoRelease
);
56 assert (start
<= end
);
57 assert (bufstart
<= bufend
);
58 assert (bufstart
>= start
);
59 assert (bufend
<= end
);
61 block
= block
->p_next
;
65 static void block_Invalidate (block_t
*block
)
69 block
->pf_release
= BlockNoRelease
;
72 # define block_Check(b) ((void)(b))
73 # define block_Invalidate(b) ((void)(b))
76 void block_Init( block_t
*restrict b
, void *buf
, size_t size
)
78 /* Fill all fields to their default */
87 b
->i_dts
= VLC_TS_INVALID
;
90 b
->pf_release
= BlockNoRelease
;
94 static void block_generic_Release (block_t
*block
)
96 /* That is always true for blocks allocated with block_Alloc(). */
97 assert (block
->p_start
== (unsigned char *)(block
+ 1));
98 block_Invalidate (block
);
102 static void BlockMetaCopy( block_t
*restrict out
, const block_t
*in
)
104 out
->p_next
= in
->p_next
;
105 out
->i_nb_samples
= in
->i_nb_samples
;
106 out
->i_dts
= in
->i_dts
;
107 out
->i_pts
= in
->i_pts
;
108 out
->i_flags
= in
->i_flags
;
109 out
->i_length
= in
->i_length
;
112 /** Initial memory alignment of data block.
113 * @note This must be a multiple of sizeof(void*) and a power of two.
114 * libavcodec AVX optimizations require at least 32-bytes. */
115 #define BLOCK_ALIGN 32
117 /** Initial reserved header and footer size. */
118 #define BLOCK_PADDING 32
120 block_t
*block_Alloc (size_t size
)
122 if (unlikely(size
>> 27))
128 /* 2 * BLOCK_PADDING: pre + post padding */
129 const size_t alloc
= sizeof (block_t
) + BLOCK_ALIGN
+ (2 * BLOCK_PADDING
)
131 if (unlikely(alloc
<= size
))
134 block_t
*b
= malloc (alloc
);
135 if (unlikely(b
== NULL
))
138 block_Init (b
, b
+ 1, alloc
- sizeof (*b
));
139 static_assert ((BLOCK_PADDING
% BLOCK_ALIGN
) == 0,
140 "BLOCK_PADDING must be a multiple of BLOCK_ALIGN");
141 b
->p_buffer
+= BLOCK_PADDING
+ BLOCK_ALIGN
- 1;
142 b
->p_buffer
= (void *)(((uintptr_t)b
->p_buffer
) & ~(BLOCK_ALIGN
- 1));
144 b
->pf_release
= block_generic_Release
;
148 block_t
*block_TryRealloc (block_t
*p_block
, ssize_t i_prebody
, size_t i_body
)
150 block_Check( p_block
);
152 /* Corner case: empty block requested */
153 if( i_prebody
<= 0 && i_body
<= (size_t)(-i_prebody
) )
154 i_prebody
= i_body
= 0;
156 assert( p_block
->p_start
<= p_block
->p_buffer
);
157 assert( p_block
->p_start
+ p_block
->i_size
158 >= p_block
->p_buffer
+ p_block
->i_buffer
);
160 /* First, shrink payload */
162 /* Pull payload start */
165 if( p_block
->i_buffer
>= (size_t)-i_prebody
)
167 p_block
->p_buffer
-= i_prebody
;
168 p_block
->i_buffer
+= i_prebody
;
170 else /* Discard current payload entirely */
171 p_block
->i_buffer
= 0;
176 /* Trim payload end */
177 if( p_block
->i_buffer
> i_body
)
178 p_block
->i_buffer
= i_body
;
180 size_t requested
= i_prebody
+ i_body
;
182 if( p_block
->i_buffer
== 0 )
183 { /* Corner case: nothing to preserve */
184 if( requested
<= p_block
->i_size
)
185 { /* Enough room: recycle buffer */
186 size_t extra
= p_block
->i_size
- requested
;
188 p_block
->p_buffer
= p_block
->p_start
+ (extra
/ 2);
189 p_block
->i_buffer
= requested
;
193 /* Not enough room: allocate a new buffer */
194 block_t
*p_rea
= block_Alloc( requested
);
198 BlockMetaCopy( p_rea
, p_block
);
199 block_Release( p_block
);
203 uint8_t *p_start
= p_block
->p_start
;
204 uint8_t *p_end
= p_start
+ p_block
->i_size
;
206 /* Second, reallocate the buffer if we lack space. */
207 assert( i_prebody
>= 0 );
208 if( (size_t)(p_block
->p_buffer
- p_start
) < (size_t)i_prebody
209 || (size_t)(p_end
- p_block
->p_buffer
) < i_body
)
211 block_t
*p_rea
= block_Alloc( requested
);
215 memcpy( p_rea
->p_buffer
+ i_prebody
, p_block
->p_buffer
,
217 BlockMetaCopy( p_rea
, p_block
);
218 block_Release( p_block
);
222 /* Third, expand payload */
224 /* Push payload start */
227 p_block
->p_buffer
-= i_prebody
;
228 p_block
->i_buffer
+= i_prebody
;
233 /* Expand payload to requested size */
234 p_block
->i_buffer
= i_body
;
239 block_t
*block_Realloc (block_t
*block
, ssize_t prebody
, size_t body
)
241 block_t
*rea
= block_TryRealloc (block
, prebody
, body
);
243 block_Release(block
);
247 static void block_heap_Release (block_t
*block
)
249 block_Invalidate (block
);
250 free (block
->p_start
);
254 block_t
*block_heap_Alloc (void *addr
, size_t length
)
256 block_t
*block
= malloc (sizeof (*block
));
263 block_Init (block
, addr
, length
);
264 block
->pf_release
= block_heap_Release
;
269 # include <sys/mman.h>
271 static void block_mmap_Release (block_t
*block
)
273 block_Invalidate (block
);
274 munmap (block
->p_start
, block
->i_size
);
278 block_t
*block_mmap_Alloc (void *addr
, size_t length
)
280 if (addr
== MAP_FAILED
)
283 long page_mask
= sysconf(_SC_PAGESIZE
) - 1;
284 size_t left
= ((uintptr_t)addr
) & page_mask
;
285 size_t right
= (-length
) & page_mask
;
287 block_t
*block
= malloc (sizeof (*block
));
290 munmap (addr
, length
);
294 block_Init (block
, ((char *)addr
) - left
, left
+ length
+ right
);
295 block
->p_buffer
= addr
;
296 block
->i_buffer
= length
;
297 block
->pf_release
= block_mmap_Release
;
301 block_t
*block_mmap_Alloc (void *addr
, size_t length
)
303 (void)addr
; (void)length
; return NULL
;
307 #ifdef HAVE_SYS_SHM_H
308 # include <sys/shm.h>
310 typedef struct block_shm_t
316 static void block_shm_Release (block_t
*block
)
318 block_shm_t
*p_sys
= (block_shm_t
*)block
;
320 shmdt (p_sys
->base_addr
);
324 block_t
*block_shm_Alloc (void *addr
, size_t length
)
326 block_shm_t
*block
= malloc (sizeof (*block
));
327 if (unlikely(block
== NULL
))
333 block_Init (&block
->self
, (uint8_t *)addr
, length
);
334 block
->self
.pf_release
= block_shm_Release
;
335 block
->base_addr
= addr
;
339 block_t
*block_shm_Alloc (void *addr
, size_t length
)
341 (void) addr
; (void) length
;
351 ssize_t
pread (int fd
, void *buf
, size_t count
, off_t offset
)
353 HANDLE handle
= (HANDLE
)(intptr_t)_get_osfhandle (fd
);
354 if (handle
== INVALID_HANDLE_VALUE
)
357 OVERLAPPED olap
= {.Offset
= offset
, .OffsetHigh
= (offset
>> 32)};
359 /* This braindead API will override the file pointer even if we specify
360 * an explicit read offset... So do not expect this to mix well with
361 * regular read() calls. */
362 if (ReadFile (handle
, buf
, count
, &written
, &olap
))
368 block_t
*block_File(int fd
, bool write
)
373 /* First, get the file size */
377 /* st_size is meaningful for regular files, shared memory and typed memory.
378 * It's also meaning for symlinks, but that's not possible with fstat().
379 * In other cases, it's undefined, and we should really not go further. */
381 # define S_TYPEISSHM( buf ) (0)
383 if (S_ISDIR (st
.st_mode
))
388 if (!S_ISREG (st
.st_mode
) && !S_TYPEISSHM (&st
))
394 /* Prevent an integer overflow in mmap() and malloc() */
395 if ((uintmax_t)st
.st_size
>= SIZE_MAX
)
400 length
= (size_t)st
.st_size
;
405 int prot
= PROT_READ
| (write
? PROT_WRITE
: 0);
406 int flags
= write
? MAP_PRIVATE
: MAP_SHARED
;
407 void *addr
= mmap(NULL
, length
, prot
, flags
, fd
, 0);
409 if (addr
!= MAP_FAILED
)
410 return block_mmap_Alloc (addr
, length
);
414 /* If mmap() is not implemented by the OS _or_ the filesystem... */
415 block_t
*block
= block_Alloc (length
);
418 block_cleanup_push (block
);
420 for (size_t i
= 0; i
< length
;)
422 ssize_t len
= pread (fd
, block
->p_buffer
+ i
, length
- i
, i
);
425 block_Release (block
);
435 block_t
*block_FilePath(const char *path
, bool write
)
437 /* NOTE: Writeable shared mappings are not supported here. So there are no
438 * needs to open the file for writing (even if the mapping is writable). */
439 int fd
= vlc_open (path
, O_RDONLY
);
443 block_t
*block
= block_File(fd
, write
);