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 block_Check (block_t
*block
)
43 unsigned char *start
= block
->p_start
;
44 unsigned char *end
= block
->p_start
+ block
->i_size
;
45 unsigned char *bufstart
= block
->p_buffer
;
46 unsigned char *bufend
= block
->p_buffer
+ block
->i_buffer
;
48 assert (start
<= end
);
49 assert (bufstart
<= bufend
);
50 assert (bufstart
>= start
);
51 assert (bufend
<= end
);
53 block
= block
->p_next
;
57 # define block_Check(b) ((void)(b))
60 block_t
*block_Init(block_t
*restrict b
, const struct vlc_block_callbacks
*cbs
,
61 void *buf
, size_t size
)
63 /* Fill all fields to their default */
72 b
->i_dts
= VLC_TICK_INVALID
;
78 static void block_generic_Release (block_t
*block
)
80 /* That is always true for blocks allocated with block_Alloc(). */
81 assert (block
->p_start
== (unsigned char *)(block
+ 1));
85 static const struct vlc_block_callbacks block_generic_cbs
=
87 block_generic_Release
,
90 static void BlockMetaCopy( block_t
*restrict out
, const block_t
*in
)
92 out
->p_next
= in
->p_next
;
93 out
->i_nb_samples
= in
->i_nb_samples
;
94 out
->i_dts
= in
->i_dts
;
95 out
->i_pts
= in
->i_pts
;
96 out
->i_flags
= in
->i_flags
;
97 out
->i_length
= in
->i_length
;
100 /** Initial memory alignment of data block.
101 * @note This must be a multiple of sizeof(void*) and a power of two.
102 * libavcodec AVX optimizations require at least 32-bytes. */
103 #define BLOCK_ALIGN 32
105 /** Initial reserved header and footer size. */
106 #define BLOCK_PADDING 32
108 block_t
*block_Alloc (size_t size
)
110 if (unlikely(size
>> 27))
116 /* 2 * BLOCK_PADDING: pre + post padding */
117 const size_t alloc
= sizeof (block_t
) + BLOCK_ALIGN
+ (2 * BLOCK_PADDING
)
119 if (unlikely(alloc
<= size
))
122 block_t
*b
= malloc (alloc
);
123 if (unlikely(b
== NULL
))
126 block_Init(b
, &block_generic_cbs
, b
+ 1, alloc
- sizeof (*b
));
127 static_assert ((BLOCK_PADDING
% BLOCK_ALIGN
) == 0,
128 "BLOCK_PADDING must be a multiple of BLOCK_ALIGN");
129 b
->p_buffer
+= BLOCK_PADDING
+ BLOCK_ALIGN
- 1;
130 b
->p_buffer
= (void *)(((uintptr_t)b
->p_buffer
) & ~(BLOCK_ALIGN
- 1));
135 void block_Release(block_t
*block
)
138 block
->p_next
= NULL
;
141 block
->cbs
->free(block
);
144 block_t
*block_TryRealloc (block_t
*p_block
, ssize_t i_prebody
, size_t i_body
)
146 block_Check( p_block
);
148 /* Corner case: empty block requested */
149 if( i_prebody
<= 0 && i_body
<= (size_t)(-i_prebody
) )
150 i_prebody
= i_body
= 0;
152 assert( p_block
->p_start
<= p_block
->p_buffer
);
153 assert( p_block
->p_start
+ p_block
->i_size
154 >= p_block
->p_buffer
+ p_block
->i_buffer
);
156 /* First, shrink payload */
158 /* Pull payload start */
161 if( p_block
->i_buffer
>= (size_t)-i_prebody
)
163 p_block
->p_buffer
-= i_prebody
;
164 p_block
->i_buffer
+= i_prebody
;
166 else /* Discard current payload entirely */
167 p_block
->i_buffer
= 0;
172 /* Trim payload end */
173 if( p_block
->i_buffer
> i_body
)
174 p_block
->i_buffer
= i_body
;
176 size_t requested
= i_prebody
+ i_body
;
178 if( p_block
->i_buffer
== 0 )
179 { /* Corner case: nothing to preserve */
180 if( requested
<= p_block
->i_size
)
181 { /* Enough room: recycle buffer */
182 size_t extra
= p_block
->i_size
- requested
;
184 p_block
->p_buffer
= p_block
->p_start
+ (extra
/ 2);
185 p_block
->i_buffer
= requested
;
189 /* Not enough room: allocate a new buffer */
190 block_t
*p_rea
= block_Alloc( requested
);
194 BlockMetaCopy( p_rea
, p_block
);
195 block_Release( p_block
);
199 uint8_t *p_start
= p_block
->p_start
;
200 uint8_t *p_end
= p_start
+ p_block
->i_size
;
202 /* Second, reallocate the buffer if we lack space. */
203 assert( i_prebody
>= 0 );
204 if( (size_t)(p_block
->p_buffer
- p_start
) < (size_t)i_prebody
205 || (size_t)(p_end
- p_block
->p_buffer
) < i_body
)
207 block_t
*p_rea
= block_Alloc( requested
);
211 memcpy( p_rea
->p_buffer
+ i_prebody
, p_block
->p_buffer
,
213 BlockMetaCopy( p_rea
, p_block
);
214 block_Release( p_block
);
218 /* Third, expand payload */
220 /* Push payload start */
223 p_block
->p_buffer
-= i_prebody
;
224 p_block
->i_buffer
+= i_prebody
;
229 /* Expand payload to requested size */
230 p_block
->i_buffer
= i_body
;
235 block_t
*block_Realloc (block_t
*block
, ssize_t prebody
, size_t body
)
237 block_t
*rea
= block_TryRealloc (block
, prebody
, body
);
239 block_Release(block
);
243 static void block_heap_Release (block_t
*block
)
245 free (block
->p_start
);
249 static const struct vlc_block_callbacks block_heap_cbs
=
254 block_t
*block_heap_Alloc (void *addr
, size_t length
)
256 block_t
*block
= malloc (sizeof (*block
));
263 return block_Init(block
, &block_heap_cbs
, addr
, length
);
267 # include <sys/mman.h>
269 static void block_mmap_Release (block_t
*block
)
271 munmap (block
->p_start
, block
->i_size
);
275 static const struct vlc_block_callbacks block_mmap_cbs
=
280 block_t
*block_mmap_Alloc (void *addr
, size_t length
)
282 if (addr
== MAP_FAILED
)
285 long page_mask
= sysconf(_SC_PAGESIZE
) - 1;
286 size_t left
= ((uintptr_t)addr
) & page_mask
;
287 size_t right
= (-length
) & page_mask
;
289 block_t
*block
= malloc (sizeof (*block
));
292 munmap (addr
, length
);
296 block_Init(block
, &block_mmap_cbs
,
297 ((char *)addr
) - left
, left
+ length
+ right
);
298 block
->p_buffer
= addr
;
299 block
->i_buffer
= length
;
303 block_t
*block_mmap_Alloc (void *addr
, size_t length
)
305 (void)addr
; (void)length
; return NULL
;
309 #ifdef HAVE_SYS_SHM_H
310 # include <sys/shm.h>
312 static void block_shm_Release (block_t
*block
)
314 shmdt(block
->p_start
);
318 static const struct vlc_block_callbacks block_shm_cbs
=
323 block_t
*block_shm_Alloc (void *addr
, size_t length
)
325 block_t
*block
= malloc (sizeof (*block
));
326 if (unlikely(block
== NULL
))
332 return block_Init(block
, &block_shm_cbs
, (uint8_t *)addr
, length
);
335 block_t
*block_shm_Alloc (void *addr
, size_t length
)
337 (void) addr
; (void) length
;
347 ssize_t
pread (int fd
, void *buf
, size_t count
, off_t offset
)
349 HANDLE handle
= (HANDLE
)(intptr_t)_get_osfhandle (fd
);
350 if (handle
== INVALID_HANDLE_VALUE
)
353 OVERLAPPED olap
= {.Offset
= offset
, .OffsetHigh
= (offset
>> 32)};
355 /* This braindead API will override the file pointer even if we specify
356 * an explicit read offset... So do not expect this to mix well with
357 * regular read() calls. */
358 if (ReadFile (handle
, buf
, count
, &written
, &olap
))
364 block_t
*block_File(int fd
, bool write
)
369 /* First, get the file size */
373 /* st_size is meaningful for regular files, shared memory and typed memory.
374 * It's also meaning for symlinks, but that's not possible with fstat().
375 * In other cases, it's undefined, and we should really not go further. */
377 # define S_TYPEISSHM( buf ) (0)
379 if (S_ISDIR (st
.st_mode
))
384 if (!S_ISREG (st
.st_mode
) && !S_TYPEISSHM (&st
))
390 /* Prevent an integer overflow in mmap() and malloc() */
391 if ((uintmax_t)st
.st_size
>= SIZE_MAX
)
396 length
= (size_t)st
.st_size
;
401 int prot
= PROT_READ
| (write
? PROT_WRITE
: 0);
402 int flags
= write
? MAP_PRIVATE
: MAP_SHARED
;
403 void *addr
= mmap(NULL
, length
, prot
, flags
, fd
, 0);
405 if (addr
!= MAP_FAILED
)
406 return block_mmap_Alloc (addr
, length
);
410 /* If mmap() is not implemented by the OS _or_ the filesystem... */
411 block_t
*block
= block_Alloc (length
);
414 block_cleanup_push (block
);
416 for (size_t i
= 0; i
< length
;)
418 ssize_t len
= pread (fd
, block
->p_buffer
+ i
, length
- i
, i
);
421 block_Release (block
);
431 block_t
*block_FilePath(const char *path
, bool write
)
433 /* NOTE: Writeable shared mappings are not supported here. So there are no
434 * needs to open the file for writing (even if the mapping is writable). */
435 int fd
= vlc_open (path
, O_RDONLY
);
439 block_t
*block
= block_File(fd
, write
);