block.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:15k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * block.c: Data blocks management functions
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2004 the VideoLAN team
  5.  * $Id: 099c31e226f403a3fe1dd552c37a44d56427b696 $
  6.  *
  7.  * Authors: Laurent Aimar <fenrir@videolan.org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 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 General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. /*****************************************************************************
  24.  * Preamble
  25.  *****************************************************************************/
  26. #ifdef HAVE_CONFIG_H
  27. # include "config.h"
  28. #endif
  29. #include <vlc_common.h>
  30. #include <sys/stat.h>
  31. #include "vlc_block.h"
  32. /**
  33.  * @section Block handling functions.
  34.  */
  35. /**
  36.  * Internal state for heap block.
  37.   */
  38. struct block_sys_t
  39. {
  40.     block_t     self;
  41.     size_t      i_allocated_buffer;
  42.     uint8_t     p_allocated_buffer[];
  43. };
  44. #ifndef NDEBUG
  45. static void BlockNoRelease( block_t *b )
  46. {
  47.     fprintf( stderr, "block %p has no release callback! This is a bug!n", b );
  48.     abort();
  49. }
  50. #endif
  51. void block_Init( block_t *restrict b, void *buf, size_t size )
  52. {
  53.     /* Fill all fields to their default */
  54.     b->p_next = NULL;
  55.     b->i_flags = 0;
  56.     b->i_pts =
  57.     b->i_dts = VLC_TS_INVALID;
  58.     b->i_length = 0;
  59.     b->i_rate = 0;
  60.     b->p_buffer = buf;
  61.     b->i_buffer = size;
  62. #ifndef NDEBUG
  63.     b->pf_release = BlockNoRelease;
  64. #endif
  65. }
  66. static void BlockRelease( block_t *p_block )
  67. {
  68.     free( p_block );
  69. }
  70. /* Memory alignment */
  71. #define BLOCK_ALIGN        16
  72. /* Initial size of reserved header and footer */
  73. #define BLOCK_PADDING_SIZE 32
  74. /* Maximum size of reserved footer before we release with realloc() */
  75. #define BLOCK_WASTE_SIZE   2048
  76. block_t *block_Alloc( size_t i_size )
  77. {
  78.     /* We do only one malloc
  79.      * TODO: bench if doing 2 malloc but keeping a pool of buffer is better
  80.      * TODO: use memalign
  81.      * 16 -> align on 16
  82.      * 2 * BLOCK_PADDING_SIZE -> pre + post padding
  83.      */
  84.     const size_t i_alloc = i_size + 2 * BLOCK_PADDING_SIZE + BLOCK_ALIGN;
  85.     block_sys_t *p_sys = malloc( sizeof( *p_sys ) + i_alloc );
  86.     if( p_sys == NULL )
  87.         return NULL;
  88.     /* Fill opaque data */
  89.     p_sys->i_allocated_buffer = i_alloc;
  90.     block_Init( &p_sys->self, p_sys->p_allocated_buffer + BLOCK_PADDING_SIZE
  91.                 + BLOCK_ALIGN
  92.                 - ((uintptr_t)p_sys->p_allocated_buffer % BLOCK_ALIGN),
  93.                 i_size );
  94.     p_sys->self.pf_release    = BlockRelease;
  95.     return &p_sys->self;
  96. }
  97. block_t *block_Realloc( block_t *p_block, ssize_t i_prebody, size_t i_body )
  98. {
  99.     block_sys_t *p_sys = (block_sys_t *)p_block;
  100.     ssize_t i_buffer_size = i_prebody + i_body;
  101.     if( i_buffer_size <= 0 )
  102.     {
  103.         block_Release( p_block );
  104.         return NULL;
  105.     }
  106.     if( p_block->pf_release != BlockRelease )
  107.     {
  108.         /* Special case when pf_release if overloaded
  109.          * TODO if used one day, then implement it in a smarter way */
  110.         block_t *p_dup = block_Duplicate( p_block );
  111.         block_Release( p_block );
  112.         if( !p_dup )
  113.             return NULL;
  114.         p_block = p_dup;
  115.         p_sys = (block_sys_t *)p_block;
  116.     }
  117.     /* Adjust reserved header if there is enough room */
  118.     if( p_block->p_buffer - i_prebody > p_sys->p_allocated_buffer &&
  119.         p_block->p_buffer - i_prebody < p_sys->p_allocated_buffer +
  120.         p_sys->i_allocated_buffer )
  121.     {
  122.         p_block->p_buffer -= i_prebody;
  123.         p_block->i_buffer += i_prebody;
  124.         i_prebody = 0;
  125.     }
  126.     /* Adjust payload size if there is enough room */
  127.     if( p_block->p_buffer + i_body < p_sys->p_allocated_buffer +
  128.         p_sys->i_allocated_buffer )
  129.     {
  130.         p_block->i_buffer = i_buffer_size;
  131.         i_body = 0;
  132.     }
  133.     /* Not enough room, reallocate the buffer */
  134.     if( i_body > 0 || i_prebody > 0 )
  135.     {
  136.         /* FIXME: this is really dumb, we should use realloc() */
  137.         block_t *p_rea = block_New( NULL, i_buffer_size );
  138.         if( p_rea )
  139.         {
  140.             p_rea->i_dts     = p_block->i_dts;
  141.             p_rea->i_pts     = p_block->i_pts;
  142.             p_rea->i_flags   = p_block->i_flags;
  143.             p_rea->i_length  = p_block->i_length;
  144.             p_rea->i_rate    = p_block->i_rate;
  145.             p_rea->i_samples = p_block->i_samples;
  146.             memcpy( p_rea->p_buffer + i_prebody, p_block->p_buffer,
  147.                     __MIN( p_block->i_buffer, p_rea->i_buffer - i_prebody ) );
  148.         }
  149.         block_Release( p_block );
  150.         return p_rea;
  151.     }
  152.     /* We have a very large reserved footer now? Release some of it.
  153.      * XXX it may not keep the algniment of p_buffer */
  154.     if( (p_sys->p_allocated_buffer + p_sys->i_allocated_buffer) -
  155.         (p_block->p_buffer + p_block->i_buffer) > BLOCK_WASTE_SIZE )
  156.     {
  157.         const ptrdiff_t i_prebody = p_block->p_buffer - p_sys->p_allocated_buffer;
  158.         const size_t i_new = i_prebody + p_block->i_buffer + 1 * BLOCK_PADDING_SIZE;
  159.         block_sys_t *p_new = realloc( p_sys, sizeof (*p_sys) + i_new );
  160.         if( p_new != NULL )
  161.         {
  162.             p_sys = p_new;
  163.             p_sys->i_allocated_buffer = i_new;
  164.             p_block = &p_sys->self;
  165.             p_block->p_buffer = &p_sys->p_allocated_buffer[i_prebody];
  166.         }
  167.     }
  168.     return p_block;
  169. }
  170. #ifdef HAVE_MMAP
  171. # include <sys/mman.h>
  172. typedef struct block_mmap_t
  173. {
  174.     block_t     self;
  175.     void       *base_addr;
  176.     size_t      length;
  177. } block_mmap_t;
  178. static void block_mmap_Release (block_t *block)
  179. {
  180.     block_mmap_t *p_sys = (block_mmap_t *)block;
  181.     munmap (p_sys->base_addr, p_sys->length);
  182.     free (p_sys);
  183. }
  184. /**
  185.  * Creates a block from a virtual address memory mapping (mmap).
  186.  * This is provided by LibVLC so that mmap blocks can safely be deallocated
  187.  * even after the allocating plugin has been unloaded from memory.
  188.  *
  189.  * @param addr base address of the mapping (as returned by mmap)
  190.  * @param length length (bytes) of the mapping (as passed to mmap)
  191.  * @return NULL if addr is MAP_FAILED, or an error occurred (in the later
  192.  * case, munmap(addr, length) is invoked before returning).
  193.  */
  194. block_t *block_mmap_Alloc (void *addr, size_t length)
  195. {
  196.     if (addr == MAP_FAILED)
  197.         return NULL;
  198.     block_mmap_t *block = malloc (sizeof (*block));
  199.     if (block == NULL)
  200.     {
  201.         munmap (addr, length);
  202.         return NULL;
  203.     }
  204.     block_Init (&block->self, (uint8_t *)addr, length);
  205.     block->self.pf_release = block_mmap_Release;
  206.     block->base_addr = addr;
  207.     block->length = length;
  208.     return &block->self;
  209. }
  210. #else
  211. block_t *block_mmap_Alloc (void *addr, size_t length)
  212. {
  213.     (void)addr; (void)length; return NULL;
  214. }
  215. #endif
  216. #ifdef WIN32
  217. #ifdef UNDER_CE
  218. #define _get_osfhandle(a) ((long) (a))
  219. #endif
  220. static
  221. ssize_t pread (int fd, void *buf, size_t count, off_t offset)
  222. {
  223.     HANDLE handle = (HANDLE)(intptr_t)_get_osfhandle (fd);
  224.     if (handle == INVALID_HANDLE_VALUE)
  225.         return -1;
  226.     OVERLAPPED olap = { .Offset = offset, .OffsetHigh = (offset >> 32), };
  227.     DWORD written;
  228.     /* This braindead API will override the file pointer even if we specify
  229.      * an explicit read offset... So do not expect this to mix well with
  230.      * regular read() calls. */
  231.     if (ReadFile (handle, buf, count, &written, &olap))
  232.         return written;
  233.     return -1;
  234. }
  235. #endif
  236. /**
  237.  * Loads a file into a block of memory. If possible a private file mapping is
  238.  * created. Otherwise, the file is read normally. On 32-bits platforms, this
  239.  * function will not work for very large files, due to memory space
  240.  * constraints. Cancellation point.
  241.  *
  242.  * @param fd file descriptor to load from
  243.  * @return a new block with the file content at p_buffer, and file length at
  244.  * i_buffer (release it with block_Release()), or NULL upon error (see errno).
  245.  */
  246. block_t *block_File (int fd)
  247. {
  248.     size_t length;
  249.     struct stat st;
  250.     /* First, get the file size */
  251.     if (fstat (fd, &st))
  252.         return NULL;
  253.     /* st_size is meaningful for regular files, shared memory and typed memory.
  254.      * It's also meaning for symlinks, but that's not possible with fstat().
  255.      * In other cases, it's undefined, and we should really not go further. */
  256. #ifndef S_TYPEISSHM
  257. # define S_TYPEISSHM( buf ) (0)
  258. #endif
  259.     if (S_ISDIR (st.st_mode))
  260.     {
  261.         errno = EISDIR;
  262.         return NULL;
  263.     }
  264.     if (!S_ISREG (st.st_mode) && !S_TYPEISSHM (&st))
  265.     {
  266.         errno = ESPIPE;
  267.         return NULL;
  268.     }
  269.     /* Prevent an integer overflow in mmap() and malloc() */
  270.     if (st.st_size >= SIZE_MAX)
  271.     {
  272.         errno = ENOMEM;
  273.         return NULL;
  274.     }
  275.     length = (size_t)st.st_size;
  276. #ifdef HAVE_MMAP
  277.     if (length > 0)
  278.     {
  279.         void *addr;
  280.         addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
  281.         if (addr != MAP_FAILED)
  282.             return block_mmap_Alloc (addr, length);
  283.     }
  284. #endif
  285.     /* If mmap() is not implemented by the OS _or_ the filesystem... */
  286.     block_t *block = block_Alloc (length);
  287.     if (block == NULL)
  288.         return NULL;
  289.     block_cleanup_push (block);
  290.     for (size_t i = 0; i < length;)
  291.     {
  292.         ssize_t len = pread (fd, block->p_buffer + i, length - i, i);
  293.         if (len == -1)
  294.         {
  295.             block_Release (block);
  296.             block = NULL;
  297.             break;
  298.         }
  299.         i += len;
  300.     }
  301.     vlc_cleanup_pop ();
  302.     return block;
  303. }
  304. /**
  305.  * @section Thread-safe block queue functions
  306.  */
  307. /**
  308.  * Internal state for block queues
  309.  */
  310. struct block_fifo_t
  311. {
  312.     vlc_mutex_t         lock;                         /* fifo data lock */
  313.     vlc_cond_t          wait;      /**< Wait for data */
  314.     vlc_cond_t          wait_room; /**< Wait for queue depth to shrink */
  315.     block_t             *p_first;
  316.     block_t             **pp_last;
  317.     size_t              i_depth;
  318.     size_t              i_size;
  319.     bool          b_force_wake;
  320. };
  321. block_fifo_t *block_FifoNew( void )
  322. {
  323.     block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
  324.     if( !p_fifo )
  325.         return NULL;
  326.     vlc_mutex_init( &p_fifo->lock );
  327.     vlc_cond_init( &p_fifo->wait );
  328.     vlc_cond_init( &p_fifo->wait_room );
  329.     p_fifo->p_first = NULL;
  330.     p_fifo->pp_last = &p_fifo->p_first;
  331.     p_fifo->i_depth = p_fifo->i_size = 0;
  332.     p_fifo->b_force_wake = false;
  333.     return p_fifo;
  334. }
  335. void block_FifoRelease( block_fifo_t *p_fifo )
  336. {
  337.     block_FifoEmpty( p_fifo );
  338.     vlc_cond_destroy( &p_fifo->wait_room );
  339.     vlc_cond_destroy( &p_fifo->wait );
  340.     vlc_mutex_destroy( &p_fifo->lock );
  341.     free( p_fifo );
  342. }
  343. void block_FifoEmpty( block_fifo_t *p_fifo )
  344. {
  345.     block_t *b;
  346.     vlc_mutex_lock( &p_fifo->lock );
  347.     for( b = p_fifo->p_first; b != NULL; )
  348.     {
  349.         block_t *p_next;
  350.         p_next = b->p_next;
  351.         block_Release( b );
  352.         b = p_next;
  353.     }
  354.     p_fifo->i_depth = p_fifo->i_size = 0;
  355.     p_fifo->p_first = NULL;
  356.     p_fifo->pp_last = &p_fifo->p_first;
  357.     vlc_cond_broadcast( &p_fifo->wait_room );
  358.     vlc_mutex_unlock( &p_fifo->lock );
  359. }
  360. /**
  361.  * Wait until the FIFO gets below a certain size (if needed).
  362.  *
  363.  * Note that if more than one thread writes to the FIFO, you cannot assume that
  364.  * the FIFO is actually below the requested size upon return (since another
  365.  * thread could have refilled it already). This is typically not an issue, as
  366.  * this function is meant for (relaxed) congestion control.
  367.  *
  368.  * This function may be a cancellation point and it is cancel-safe.
  369.  *
  370.  * @param fifo queue to wait on
  371.  * @param max_depth wait until the queue has no more than this many blocks
  372.  *                  (use SIZE_MAX to ignore this constraint)
  373.  * @param max_size wait until the queue has no more than this many bytes
  374.  *                  (use SIZE_MAX to ignore this constraint)
  375.  * @return nothing.
  376.  */
  377. void block_FifoPace (block_fifo_t *fifo, size_t max_depth, size_t max_size)
  378. {
  379.     vlc_testcancel ();
  380.     vlc_mutex_lock (&fifo->lock);
  381.     while ((fifo->i_depth > max_depth) || (fifo->i_size > max_size))
  382.     {
  383.          mutex_cleanup_push (&fifo->lock);
  384.          vlc_cond_wait (&fifo->wait_room, &fifo->lock);
  385.          vlc_cleanup_pop ();
  386.     }
  387.     vlc_mutex_unlock (&fifo->lock);
  388. }
  389. /**
  390.  * Immediately queue one block at the end of a FIFO.
  391.  * @param fifo queue
  392.  * @param block head of a block list to queue (may be NULL)
  393.  */
  394. size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
  395. {
  396.     size_t i_size = 0;
  397.     vlc_mutex_lock( &p_fifo->lock );
  398.     while (p_block != NULL)
  399.     {
  400.         i_size += p_block->i_buffer;
  401.         *p_fifo->pp_last = p_block;
  402.         p_fifo->pp_last = &p_block->p_next;
  403.         p_fifo->i_depth++;
  404.         p_fifo->i_size += p_block->i_buffer;
  405.         p_block = p_block->p_next;
  406.     }
  407.     /* We queued one block: wake up one read-waiting thread */
  408.     vlc_cond_signal( &p_fifo->wait );
  409.     vlc_mutex_unlock( &p_fifo->lock );
  410.     return i_size;
  411. }
  412. void block_FifoWake( block_fifo_t *p_fifo )
  413. {
  414.     vlc_mutex_lock( &p_fifo->lock );
  415.     if( p_fifo->p_first == NULL )
  416.         p_fifo->b_force_wake = true;
  417.     vlc_cond_broadcast( &p_fifo->wait );
  418.     vlc_mutex_unlock( &p_fifo->lock );
  419. }
  420. block_t *block_FifoGet( block_fifo_t *p_fifo )
  421. {
  422.     block_t *b;
  423.     vlc_testcancel( );
  424.     vlc_mutex_lock( &p_fifo->lock );
  425.     mutex_cleanup_push( &p_fifo->lock );
  426.     /* Remember vlc_cond_wait() may cause spurious wakeups
  427.      * (on both Win32 and POSIX) */
  428.     while( ( p_fifo->p_first == NULL ) && !p_fifo->b_force_wake )
  429.         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
  430.     vlc_cleanup_pop();
  431.     b = p_fifo->p_first;
  432.     p_fifo->b_force_wake = false;
  433.     if( b == NULL )
  434.     {
  435.         /* Forced wakeup */
  436.         vlc_mutex_unlock( &p_fifo->lock );
  437.         return NULL;
  438.     }
  439.     p_fifo->p_first = b->p_next;
  440.     p_fifo->i_depth--;
  441.     p_fifo->i_size -= b->i_buffer;
  442.     if( p_fifo->p_first == NULL )
  443.     {
  444.         p_fifo->pp_last = &p_fifo->p_first;
  445.     }
  446.     /* We don't know how many threads can queue new packets now. */
  447.     vlc_cond_broadcast( &p_fifo->wait_room );
  448.     vlc_mutex_unlock( &p_fifo->lock );
  449.     b->p_next = NULL;
  450.     return b;
  451. }
  452. block_t *block_FifoShow( block_fifo_t *p_fifo )
  453. {
  454.     block_t *b;
  455.     vlc_testcancel( );
  456.     vlc_mutex_lock( &p_fifo->lock );
  457.     mutex_cleanup_push( &p_fifo->lock );
  458.     while( p_fifo->p_first == NULL )
  459.         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
  460.     b = p_fifo->p_first;
  461.     vlc_cleanup_run ();
  462.     return b;
  463. }
  464. /* FIXME: not thread-safe */
  465. size_t block_FifoSize( const block_fifo_t *p_fifo )
  466. {
  467.     return p_fifo->i_size;
  468. }
  469. /* FIXME: not thread-safe */
  470. size_t block_FifoCount( const block_fifo_t *p_fifo )
  471. {
  472.     return p_fifo->i_depth;
  473. }