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 /* 2 * BLOCK_PADDING: pre + post padding */
123 const size_t alloc
= sizeof (block_t
) + BLOCK_ALIGN
+ (2 * BLOCK_PADDING
)
125 if (unlikely(alloc
<= size
))
128 block_t
*b
= malloc (alloc
);
129 if (unlikely(b
== NULL
))
132 block_Init (b
, b
+ 1, alloc
- sizeof (*b
));
133 static_assert ((BLOCK_PADDING
% BLOCK_ALIGN
) == 0,
134 "BLOCK_PADDING must be a multiple of BLOCK_ALIGN");
135 b
->p_buffer
+= BLOCK_PADDING
+ BLOCK_ALIGN
- 1;
136 b
->p_buffer
= (void *)(((uintptr_t)b
->p_buffer
) & ~(BLOCK_ALIGN
- 1));
138 b
->pf_release
= block_generic_Release
;
142 block_t
*block_TryRealloc (block_t
*p_block
, ssize_t i_prebody
, size_t i_body
)
144 block_Check( p_block
);
146 /* Corner case: empty block requested */
147 if( i_prebody
<= 0 && i_body
<= (size_t)(-i_prebody
) )
148 i_prebody
= i_body
= 0;
150 assert( p_block
->p_start
<= p_block
->p_buffer
);
151 assert( p_block
->p_start
+ p_block
->i_size
152 >= p_block
->p_buffer
+ p_block
->i_buffer
);
154 /* First, shrink payload */
156 /* Pull payload start */
159 if( p_block
->i_buffer
>= (size_t)-i_prebody
)
161 p_block
->p_buffer
-= i_prebody
;
162 p_block
->i_buffer
+= i_prebody
;
164 else /* Discard current payload entirely */
165 p_block
->i_buffer
= 0;
170 /* Trim payload end */
171 if( p_block
->i_buffer
> i_body
)
172 p_block
->i_buffer
= i_body
;
174 size_t requested
= i_prebody
+ i_body
;
176 if( p_block
->i_buffer
== 0 )
177 { /* Corner case: nothing to preserve */
178 if( requested
<= p_block
->i_size
)
179 { /* Enough room: recycle buffer */
180 size_t extra
= p_block
->i_size
- requested
;
182 p_block
->p_buffer
= p_block
->p_start
+ (extra
/ 2);
183 p_block
->i_buffer
= requested
;
187 /* Not enough room: allocate a new buffer */
188 block_t
*p_rea
= block_Alloc( requested
);
192 BlockMetaCopy( p_rea
, p_block
);
193 block_Release( p_block
);
197 uint8_t *p_start
= p_block
->p_start
;
198 uint8_t *p_end
= p_start
+ p_block
->i_size
;
200 /* Second, reallocate the buffer if we lack space. */
201 assert( i_prebody
>= 0 );
202 if( (size_t)(p_block
->p_buffer
- p_start
) < (size_t)i_prebody
203 || (size_t)(p_end
- p_block
->p_buffer
) < i_body
)
205 block_t
*p_rea
= block_Alloc( requested
);
209 memcpy( p_rea
->p_buffer
+ i_prebody
, p_block
->p_buffer
,
211 BlockMetaCopy( p_rea
, p_block
);
212 block_Release( p_block
);
216 /* Third, expand payload */
218 /* Push payload start */
221 p_block
->p_buffer
-= i_prebody
;
222 p_block
->i_buffer
+= i_prebody
;
227 /* Expand payload to requested size */
228 p_block
->i_buffer
= i_body
;
233 block_t
*block_Realloc (block_t
*block
, ssize_t prebody
, size_t body
)
235 block_t
*rea
= block_TryRealloc (block
, prebody
, body
);
237 block_Release(block
);
241 static void block_heap_Release (block_t
*block
)
243 block_Invalidate (block
);
244 free (block
->p_start
);
248 block_t
*block_heap_Alloc (void *addr
, size_t length
)
250 block_t
*block
= malloc (sizeof (*block
));
257 block_Init (block
, addr
, length
);
258 block
->pf_release
= block_heap_Release
;
263 # include <sys/mman.h>
265 static void block_mmap_Release (block_t
*block
)
267 block_Invalidate (block
);
268 munmap (block
->p_start
, block
->i_size
);
272 block_t
*block_mmap_Alloc (void *addr
, size_t length
)
274 if (addr
== MAP_FAILED
)
277 long page_mask
= sysconf(_SC_PAGESIZE
) - 1;
278 size_t left
= ((uintptr_t)addr
) & page_mask
;
279 size_t right
= (-length
) & page_mask
;
281 block_t
*block
= malloc (sizeof (*block
));
284 munmap (addr
, length
);
288 block_Init (block
, ((char *)addr
) - left
, left
+ length
+ right
);
289 block
->p_buffer
= addr
;
290 block
->i_buffer
= length
;
291 block
->pf_release
= block_mmap_Release
;
295 block_t
*block_mmap_Alloc (void *addr
, size_t length
)
297 (void)addr
; (void)length
; return NULL
;
301 #ifdef HAVE_SYS_SHM_H
302 # include <sys/shm.h>
304 typedef struct block_shm_t
310 static void block_shm_Release (block_t
*block
)
312 block_shm_t
*p_sys
= (block_shm_t
*)block
;
314 shmdt (p_sys
->base_addr
);
318 block_t
*block_shm_Alloc (void *addr
, size_t length
)
320 block_shm_t
*block
= malloc (sizeof (*block
));
321 if (unlikely(block
== NULL
))
327 block_Init (&block
->self
, (uint8_t *)addr
, length
);
328 block
->self
.pf_release
= block_shm_Release
;
329 block
->base_addr
= addr
;
333 block_t
*block_shm_Alloc (void *addr
, size_t length
)
335 (void) addr
; (void) length
;
345 ssize_t
pread (int fd
, void *buf
, size_t count
, off_t offset
)
347 HANDLE handle
= (HANDLE
)(intptr_t)_get_osfhandle (fd
);
348 if (handle
== INVALID_HANDLE_VALUE
)
351 OVERLAPPED olap
= {.Offset
= offset
, .OffsetHigh
= (offset
>> 32)};
353 /* This braindead API will override the file pointer even if we specify
354 * an explicit read offset... So do not expect this to mix well with
355 * regular read() calls. */
356 if (ReadFile (handle
, buf
, count
, &written
, &olap
))
362 block_t
*block_File(int fd
, bool write
)
367 /* First, get the file size */
371 /* st_size is meaningful for regular files, shared memory and typed memory.
372 * It's also meaning for symlinks, but that's not possible with fstat().
373 * In other cases, it's undefined, and we should really not go further. */
375 # define S_TYPEISSHM( buf ) (0)
377 if (S_ISDIR (st
.st_mode
))
382 if (!S_ISREG (st
.st_mode
) && !S_TYPEISSHM (&st
))
388 /* Prevent an integer overflow in mmap() and malloc() */
389 if ((uintmax_t)st
.st_size
>= SIZE_MAX
)
394 length
= (size_t)st
.st_size
;
399 int prot
= PROT_READ
| (write
? PROT_WRITE
: 0);
400 int flags
= write
? MAP_PRIVATE
: MAP_SHARED
;
401 void *addr
= mmap(NULL
, length
, prot
, flags
, fd
, 0);
403 if (addr
!= MAP_FAILED
)
404 return block_mmap_Alloc (addr
, length
);
408 /* If mmap() is not implemented by the OS _or_ the filesystem... */
409 block_t
*block
= block_Alloc (length
);
412 block_cleanup_push (block
);
414 for (size_t i
= 0; i
< length
;)
416 ssize_t len
= pread (fd
, block
->p_buffer
+ i
, length
- i
, i
);
419 block_Release (block
);
429 block_t
*block_FilePath(const char *path
, bool write
)
431 /* NOTE: Writeable shared mappings are not supported here. So there are no
432 * needs to open the file for writing (even if the mapping is writable). */
433 int fd
= vlc_open (path
, O_RDONLY
);
437 block_t
*block
= block_File(fd
, write
);