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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * mmap.c: memory-mapped file input
  3.  *****************************************************************************
  4.  * Copyright © 2007-2008 Rémi Denis-Courmont
  5.  * $Id: 8a36040aad316eb673e62d5e9efda5c65f322080 $
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  20.  *****************************************************************************/
  21. #ifdef HAVE_CONFIG_H
  22. # include <config.h>
  23. #endif
  24. #include <vlc_common.h>
  25. #include <vlc_plugin.h>
  26. #include <vlc_access.h>
  27. #include <vlc_input.h>
  28. #include <vlc_charset.h>
  29. #include <vlc_dialog.h>
  30. #include <assert.h>
  31. #include <errno.h>
  32. #include <sys/types.h>
  33. #include <unistd.h>
  34. #include <fcntl.h>
  35. #include <sys/stat.h>
  36. #include <sys/mman.h>
  37. #define FILE_MMAP_TEXT N_("Use file memory mapping")
  38. #define FILE_MMAP_LONGTEXT N_( 
  39.     "Try to use memory mapping to read files and block devices." )
  40. #ifndef NDEBUG
  41. /*# define MMAP_DEBUG 1*/
  42. #endif
  43. static int Open (vlc_object_t *);
  44. static void Close (vlc_object_t *);
  45. vlc_module_begin ()
  46.     set_shortname (N_("MMap"))
  47.     set_description (N_("Memory-mapped file input"))
  48.     set_category (CAT_INPUT)
  49.     set_subcategory (SUBCAT_INPUT_ACCESS)
  50.     set_capability ("access", 52)
  51.     add_shortcut ("file")
  52.     set_callbacks (Open, Close)
  53.     add_bool ("file-mmap", false, NULL,
  54.               FILE_MMAP_TEXT, FILE_MMAP_LONGTEXT, true)
  55. vlc_module_end ()
  56. static block_t *Block (access_t *);
  57. static int Seek (access_t *, int64_t);
  58. static int Control (access_t *, int, va_list);
  59. struct access_sys_t
  60. {
  61.     size_t page_size;
  62.     size_t mtu;
  63.     int    fd;
  64. };
  65. #define MMAP_SIZE (1 << 20)
  66. static int Open (vlc_object_t *p_this)
  67. {
  68.     access_t *p_access = (access_t *)p_this;
  69.     access_sys_t *p_sys;
  70.     const char *path = p_access->psz_path;
  71.     int fd;
  72.     assert ((INT64_C(1) << 63) == ((off_t)(INT64_C(1) << 63)));
  73.     if (!var_CreateGetBool (p_this, "file-mmap"))
  74.         return VLC_EGENERIC; /* disabled */
  75.     STANDARD_BLOCK_ACCESS_INIT;
  76.     if (!strcmp (p_access->psz_path, "-"))
  77.         fd = dup (0);
  78.     else
  79.     {
  80.         msg_Dbg (p_access, "opening file %s", path);
  81.         fd = utf8_open (path, O_RDONLY | O_NOCTTY, 0666);
  82.     }
  83.     if (fd == -1)
  84.     {
  85.         msg_Warn (p_access, "cannot open %s: %m", path);
  86.         goto error;
  87.     }
  88.     /* mmap() is only safe for regular and block special files.
  89.      * For other types, it may be some idiosyncrasic interface (e.g. packet
  90.      * sockets are a good example), if it works at all. */
  91.     struct stat st;
  92.     if (fstat (fd, &st))
  93.     {
  94.         msg_Err (p_access, "cannot stat %s: %m", path);
  95.         goto error;
  96.     }
  97.     if (!S_ISREG (st.st_mode) && !S_ISBLK (st.st_mode))
  98.     {
  99.         msg_Dbg (p_access, "skipping non-regular file %s", path);
  100.         goto error;
  101.     }
  102. #if defined(HAVE_FCNTL_H)
  103.     /* We'd rather use any available memory for reading ahead
  104.      * than for caching what we've already mmap'ed */
  105. # if defined(F_RDAHEAD)
  106.     fcntl (fd, F_RDAHEAD, 1);
  107. # endif
  108. # if defined(F_NOCACHE)
  109.     fcntl (fd, F_NOCACHE, 1);
  110. # endif
  111. #endif
  112.     /* Autodetect mmap() support */
  113.     if (st.st_size > 0)
  114.     {
  115.         void *addr = mmap (NULL, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
  116.         if (addr != MAP_FAILED)
  117.             munmap (addr, 1);
  118.         else
  119.             goto error;
  120.     }
  121.     p_sys->page_size = sysconf (_SC_PAGE_SIZE);
  122.     p_sys->mtu = MMAP_SIZE;
  123.     if (p_sys->mtu < p_sys->page_size)
  124.         p_sys->mtu = p_sys->page_size;
  125.     p_sys->fd = fd;
  126.     p_access->info.i_size = st.st_size;
  127. #ifdef HAVE_POSIX_FADVISE    
  128.     posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
  129. #endif 
  130.     return VLC_SUCCESS;
  131. error:
  132.     if (fd != -1)
  133.         close (fd);
  134.     free (p_sys);
  135.     return VLC_EGENERIC;
  136. }
  137. static void Close (vlc_object_t * p_this)
  138. {
  139.     access_t *p_access = (access_t *)p_this;
  140.     access_sys_t *p_sys = p_access->p_sys;
  141.     close (p_sys->fd); /* don't care about error when only reading */
  142.     free (p_sys);
  143. }
  144. static block_t *Block (access_t *p_access)
  145. {
  146.     access_sys_t *p_sys = p_access->p_sys;
  147.     /* Check if file size changed... */
  148.     struct stat st;
  149.     if ((fstat (p_sys->fd, &st) == 0)
  150.      && (st.st_size != p_access->info.i_size))
  151.     {
  152.         p_access->info.i_size = st.st_size;
  153.         p_access->info.i_update |= INPUT_UPDATE_SIZE;
  154.     }
  155.     if ((uint64_t)p_access->info.i_pos >= (uint64_t)p_access->info.i_size)
  156.     {
  157.         /* We are at end of file */
  158.         p_access->info.b_eof = true;
  159.         msg_Dbg (p_access, "at end of memory mapped file");
  160.         return NULL;
  161.     }
  162. #ifdef MMAP_DEBUG
  163.     int64_t dbgpos = lseek (p_sys->fd, 0, SEEK_CUR);
  164.     if (dbgpos != p_access->info.i_pos)
  165.         msg_Err (p_access, "position: 0x%016"PRIx64" instead of 0x%016"PRIx64,
  166.                  p_access->info.i_pos, dbgpos);
  167. #endif
  168.     const uintptr_t page_mask = p_sys->page_size - 1;
  169.     /* Start the mapping on a page boundary: */
  170.     off_t  outer_offset = p_access->info.i_pos & ~(off_t)page_mask;
  171.     /* Skip useless bytes at the beginning of the first page: */
  172.     size_t inner_offset = p_access->info.i_pos & page_mask;
  173.     /* Map no more bytes than remain: */
  174.     size_t length = p_sys->mtu;
  175.     if (outer_offset + length > p_access->info.i_size)
  176.         length = p_access->info.i_size - outer_offset;
  177.     assert (outer_offset <= p_access->info.i_pos);          /* and */
  178.     assert (p_access->info.i_pos < p_access->info.i_size); /* imply */
  179.     assert (outer_offset < p_access->info.i_size);         /* imply */
  180.     assert (length > 0);
  181.     /* NOTE: We use PROT_WRITE and MAP_PRIVATE so that the block can be
  182.      * modified down the chain, without messing up with the underlying
  183.      * original file. This does NOT need open write permission. */
  184.     void *addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE
  185. #ifdef MAP_NO_CACHE
  186.                        | MAP_NOCACHE
  187. #endif
  188.                        , p_sys->fd, outer_offset);
  189.     if (addr == MAP_FAILED)
  190.     {
  191.         msg_Err (p_access, "memory mapping failed (%m)");
  192.         dialog_Fatal (p_access, _("File reading failed"), "%s",
  193.                         _("VLC could not read the file."));
  194.         goto fatal;
  195.     }
  196. #ifdef HAVE_POSIX_MADVISE    
  197.     posix_madvise (addr, length, POSIX_MADV_SEQUENTIAL);
  198. #endif
  199.     block_t *block = block_mmap_Alloc (addr, length);
  200.     if (block == NULL)
  201.         goto fatal;
  202.     block->p_buffer += inner_offset;
  203.     block->i_buffer -= inner_offset;
  204. #ifdef MMAP_DEBUG
  205.     msg_Dbg (p_access, "mapped 0x%zx bytes at %p from offset 0x%"PRIx64,
  206.              length, addr, (uint64_t)outer_offset);
  207.     /* Compare normal I/O with memory mapping */
  208.     char *buf = malloc (block->i_buffer);
  209.     ssize_t i_read = read (p_sys->fd, buf, block->i_buffer);
  210.     if (i_read != (ssize_t)block->i_buffer)
  211.         msg_Err (p_access, "read %zd instead of %zu bytes", i_read,
  212.                  block->i_buffer);
  213.     if (memcmp (buf, block->p_buffer, block->i_buffer))
  214.         msg_Err (p_access, "inconsistent data buffer");
  215.     free (buf);
  216. #endif
  217.     p_access->info.i_pos = outer_offset + length;
  218.     return block;
  219. fatal:
  220.     p_access->info.b_eof = true;
  221.     return NULL;
  222. }
  223. static int Seek (access_t *p_access, int64_t i_pos)
  224. {
  225. #ifdef MMAP_DEBUG
  226.     lseek (p_access->p_sys->fd, i_pos, SEEK_SET);
  227. #endif
  228.     p_access->info.i_pos = i_pos;
  229.     p_access->info.b_eof = false;
  230.     return VLC_SUCCESS;
  231. }
  232. static int Control (access_t *p_access, int query, va_list args)
  233. {
  234.     switch (query)
  235.     {
  236.         case ACCESS_CAN_SEEK:
  237.         case ACCESS_CAN_FASTSEEK:
  238.         case ACCESS_CAN_PAUSE:
  239.         case ACCESS_CAN_CONTROL_PACE:
  240.             *((bool *)va_arg (args, bool *)) = true;
  241.             return VLC_SUCCESS;
  242.         case ACCESS_GET_PTS_DELAY:
  243.         {
  244.             int delay_ms = var_CreateGetInteger (p_access, "file-caching");
  245.             *((int64_t *)va_arg (args, int64_t *)) = delay_ms * INT64_C (1000);
  246.             return VLC_SUCCESS;
  247.         }
  248.         case ACCESS_GET_TITLE_INFO:
  249.         case ACCESS_GET_META:
  250.             break;
  251.         case ACCESS_SET_PAUSE_STATE:
  252.             return VLC_SUCCESS;
  253.         case ACCESS_SET_TITLE:
  254.         case ACCESS_SET_SEEKPOINT:
  255.         case ACCESS_SET_PRIVATE_ID_STATE:
  256.         case ACCESS_SET_PRIVATE_ID_CA:
  257.         case ACCESS_GET_PRIVATE_ID_STATE:
  258.         case ACCESS_GET_CONTENT_TYPE:
  259.             break;
  260.         default:
  261.             msg_Warn (p_access, "unimplemented query %d in control", query);
  262.     }
  263.     return VLC_EGENERIC;
  264. }