LCOV - code coverage report
Current view: top level - src - filecollection.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 90 90 100.0 %
Date: 2024-06-15 08:26:09 Functions: 19 20 95.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Zipios -- a small C++ library that provides easy access to .zip files.
       3             : 
       4             :   Copyright (C) 2000-2007  Thomas Sondergaard
       5             :   Copyright (c) 2015-2022  Made to Order Software Corp.  All Rights Reserved
       6             : 
       7             :   This library is free software; you can redistribute it and/or
       8             :   modify it under the terms of the GNU Lesser General Public
       9             :   License as published by the Free Software Foundation; either
      10             :   version 2.1 of the License, or (at your option) any later version.
      11             : 
      12             :   This library 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 GNU
      15             :   Lesser General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Lesser General Public
      18             :   License along with this library; if not, write to the Free Software
      19             :   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      20             : */
      21             : 
      22             : /** \file
      23             :  * \brief Implementation of zipios::FileCollection.
      24             :  *
      25             :  * This file defines all the FileCollection functions that are not
      26             :  * pure virtual. The file also includes documentation for all those
      27             :  * functions.
      28             :  */
      29             : 
      30             : #include "zipios/filecollection.hpp"
      31             : 
      32             : #include "zipios/zipiosexceptions.hpp"
      33             : 
      34             : #include <algorithm>
      35             : 
      36             : 
      37             : namespace zipios
      38             : {
      39             : 
      40             : 
      41             : 
      42             : namespace
      43             : {
      44             : 
      45             : /** \brief A default filename for unnamed collections.
      46             :  *
      47             :  * This string represents the default m_filename value when a collection
      48             :  * is created without a filename.
      49             :  */
      50             : char const * g_default_filename = "-";
      51             : 
      52             : 
      53             : /** \brief Class object used with the std::find_if() function.
      54             :  *
      55             :  * This function object is used with the STL find_if algorithm to
      56             :  * find a FileEntry in a container, which name (as obtained with
      57             :  * FileEntry::getName()) is identical to the name specified in the
      58             :  * MatchName constructor.
      59             :  */
      60             : class MatchName
      61             : {
      62             : public:
      63             :     /** \brief Initialize a MatchName object.
      64             :      *
      65             :      * This function saves the name to search in the FileCollection.
      66             :      *
      67             :      * This class expect the name to be a full path and file name
      68             :      * with extension. The full name has to match.
      69             :      *
      70             :      * \param[in] name  The name of the file being searched.
      71             :      */
      72      188022 :     explicit MatchName(std::string const & name)
      73      188022 :         : m_name(name)
      74             :     {
      75      188022 :     }
      76             : 
      77             :     /** \brief Compare an entry to this MatchName.
      78             :      *
      79             :      * This function compares the full name of the entry with the
      80             :      * saved full name. If equal, then it returns true. It is used
      81             :      * with the std::find_if() function.
      82             :      *
      83             :      * \todo
      84             :      * We could transform that with lambda at some point.
      85             :      *
      86             :      * \param[in] entry  The entry to compare with the MatchName.
      87             :      *
      88             :      * \return true if the name of the entry matches the MatchName.
      89             :      */
      90    38979814 :     bool operator() (FileEntry::pointer_t entry) const
      91             :     {
      92    38979814 :         return entry->getName() == m_name;
      93             :     }
      94             : 
      95             : private:
      96             :     std::string const       m_name;
      97             : };
      98             : 
      99             : 
     100             : /** \brief Class object used with the std::find_if() function.
     101             :  *
     102             :  * This function object is used with the STL find_if algorithm to
     103             :  * find a FileEntry in a container, which name (as obtained with
     104             :  * FileEntry::getFileName()) is identical to the name specified in the
     105             :  * MatchFileName constructor.
     106             :  *
     107             :  * \warning
     108             :  * The file name cannot include a '/' in this case or the search will
     109             :  * always fail.
     110             :  */
     111             : class MatchFileName
     112             : {
     113             : public:
     114             :     /** \brief Initialize a MatchFileName object.
     115             :      *
     116             :      * This function saves the base name to search in the
     117             :      * FileCollection.
     118             :      *
     119             :      * This class expect the name to be a base file name, eventually with
     120             :      * an extension. If the name includes a slash then the search will
     121             :      * always fail.
     122             :      *
     123             :      * \param[in] name  The name of the file being searched.
     124             :      */
     125        9343 :     explicit MatchFileName(std::string const & name)
     126        9343 :         : m_name(name)
     127             :     {
     128        9343 :     }
     129             : 
     130             :     /** \brief Compare an entry to this MatchFileName.
     131             :      *
     132             :      * This function compares the base name of the entry with the
     133             :      * saved base name. If equal, then it returns true. It is used
     134             :      * with the std::find_if() function.
     135             :      *
     136             :      * \todo
     137             :      * We could transform that with lambda at some point.
     138             :      *
     139             :      * \param[in] entry  The entry to compare with the MatchFileName.
     140             :      *
     141             :      * \return true if the name of the entry matches the MatchFileName.
     142             :      */
     143     4805489 :     bool operator() (FileEntry::pointer_t entry) const
     144             :     {
     145     4805489 :         return entry->getFileName() == m_name;
     146             :     }
     147             : 
     148             : private:
     149             :     std::string const       m_name;
     150             : };
     151             : 
     152             : 
     153             : } // no name namespace
     154             : 
     155             : 
     156             : 
     157             : /** \class FileCollection
     158             :  * \brief Base class for various file collections.
     159             :  *
     160             :  * FileCollection is an abstract base class that represents a
     161             :  * collection of files. The specializations of FileCollection
     162             :  * represents different origins of file collections, such as
     163             :  * directories, simple filename lists and compressed archives.
     164             :  */
     165             : 
     166             : 
     167             : /** \typedef std::shared_ptr<std::istream> FileCollection::stream_pointer_t;
     168             :  * \brief A shared pointer to an input stream.
     169             :  *
     170             :  * This type of pointer is used whenever you retrieve an input stream
     171             :  * from a file collection such as the ZipFile class. Having shared
     172             :  * pointers ensures that the pointers can be shared between various
     173             :  * functions and it gets deleted in the end.
     174             :  */
     175             : 
     176             : 
     177             : /** \fn stream_pointer_t FileCollection::getInputStream(std::string const & entry_name, MatchPath matchpath = MatchPath::MATCH);
     178             :  * \brief Retrieve pointer to an istream.
     179             :  *
     180             :  * This function returns a shared pointer to an istream defined from the
     181             :  * named entry which is expected to be available in this collection.
     182             :  *
     183             :  * The function returns a NULL pointer if there is no entry with the
     184             :  * specified name in this FileCollection.
     185             :  *
     186             :  * Note that the function returns a smart pointer to an istream. In
     187             :  * general the FileCollection will not hold that pointer meaning that
     188             :  * if you call getInputStream() multiple times with the same
     189             :  * \p entry_name parameter, you get distinct istream instances each
     190             :  * time.
     191             :  *
     192             :  * By default the \p entry_name parameter is expected to match the full
     193             :  * path and filename (MatchPath::MATCH). If you are looking for a file
     194             :  * and want to ignore the directory name, set the matchpath parameter
     195             :  * to MatchPath::IGNORE.
     196             :  *
     197             :  * \warning
     198             :  * In version 1.0 there was a version of the function accepting a
     199             :  * FileEntry instead of a filename. That function was simply calling
     200             :  * this function with file_entry->getName() and MatchPath::MATCH so
     201             :  * you can convert the call with:
     202             :  *
     203             :  * \code
     204             :  *      // old code:
     205             :  *      ConstEntryPointer ent = zf.getEntry("file2.txt", FileCollection::IGNORE);
     206             :  *      if(ent)
     207             :  *      {
     208             :  *          auto_ptr<istream> is(getInputStream(ent));
     209             :  *          if(is)
     210             :  *          {
     211             :  *              // got access to the file in the archive
     212             :  *              ...
     213             :  *          }
     214             :  *      }
     215             :  *
     216             :  *      // new code:
     217             :  *      zipios::FileEntry::pointer_t ent(zf.getEntry(argv[2], zipios::FileCollection::MatchPath::IGNORE));
     218             :  *      if(ent)
     219             :  *      {
     220             :  *          zipios::ZipFile::stream_pointer_t is(zf.getInputStream(ent->getName()));
     221             :  *          if(is)
     222             :  *          {
     223             :  *              // got access to the file in the archive
     224             :  *              ...
     225             :  *          }
     226             :  *      }
     227             :  * \endcode
     228             :  *
     229             :  * \par
     230             :  * There are two reasons for the change: (1) the function really just called
     231             :  * the other with getName() and thus there was no reason to have two
     232             :  * functions; and (2) the function did NOT test whether the entry was one
     233             :  * that this collection owned making it feel like you could call the
     234             :  * getInputStream() function of collection A with entry of collection B
     235             :  * and still get a valid stream.
     236             :  *
     237             :  * \param[in] entry_name  The name of the file to search in the collection.
     238             :  * \param[in] matchpath  Whether the full path or just the filename is matched.
     239             :  *
     240             :  * \return A shared pointer to an open istream for the specified entry.
     241             :  *
     242             :  * \sa CollectionCollection
     243             :  * \sa DirectoryCollection
     244             :  * \sa ZipFile
     245             :  */
     246             : 
     247             : 
     248             : /** \fn FileCollection::pointer_t FileCollection::clone() const;
     249             :  * \brief Create a clone of this object.
     250             :  *
     251             :  * This function creates a heap allocated clone of the object this
     252             :  * method is called for.
     253             :  *
     254             :  * \return A shared pointer to a copy of the object this method is called for.
     255             :  */
     256             : 
     257             : 
     258             : /** \brief Initializes a FileCollection object.
     259             :  *
     260             :  * This FileCollection constructor initializes the object and
     261             :  * mark it as invalid. In most cases an invalid collection cannot
     262             :  * be used for anything. You may make it valid by copying a valid
     263             :  * collection in it.
     264             :  *
     265             :  * By default the FileCollection is given the special filename "-".
     266             :  *
     267             :  * The collection is empty and marked as invalid.
     268             :  */
     269         595 : FileCollection::FileCollection(std::string const & filename)
     270         595 :     : m_filename(filename.empty() ? g_default_filename : filename)
     271             : {
     272         595 : }
     273             : 
     274             : 
     275             : /** \brief Copy a FileCollection in a new one.
     276             :  *
     277             :  * This constructor copies a file collection (\p rhs) in a new collection.
     278             :  *
     279             :  * The copy entries that all the entries from the source collection get
     280             :  * cloned in the copy. This means entries in the source or new collection
     281             :  * can be modified and it has no effect on the entries in the other
     282             :  * collection.
     283             :  *
     284             :  * \param[in] rhs  The source collection to copy in this collection.
     285             :  */
     286         101 : FileCollection::FileCollection(FileCollection const & rhs)
     287         101 :     : m_filename(rhs.m_filename)
     288         101 :     , m_valid(rhs.m_valid)
     289             : {
     290         101 :     m_entries.reserve(rhs.m_entries.size());
     291       17188 :     for(auto it = rhs.m_entries.begin(); it != rhs.m_entries.end(); ++it)
     292             :     {
     293       17087 :         m_entries.push_back((*it)->clone());
     294             :     }
     295         101 : }
     296             : 
     297             : 
     298             : /** \brief Replace the content of a collection with a copy of another collection.
     299             :  *
     300             :  * This function copies the \p rhs collection in this collection.
     301             :  *
     302             :  * Note that the entries in the this collection get released. If you still
     303             :  * have a reference to them in a shared pointer, they will not be deleted.
     304             :  *
     305             :  * The entries in \p rhs get cloned so modifying the entries in the source
     306             :  * or the destination has no effect on the entries of the other collection.
     307             :  *
     308             :  * \param[in] rhs  The source FileCollection to copy.
     309             :  *
     310             :  * \return A reference to this FileCollection object.
     311             :  */
     312          21 : FileCollection & FileCollection::operator = (FileCollection const & rhs)
     313             : {
     314          21 :     if(this != &rhs)
     315             :     {
     316          21 :         m_filename = rhs.m_filename;
     317             : 
     318          21 :         m_entries.clear();
     319          21 :         m_entries.reserve(rhs.m_entries.size());
     320        7806 :         for(auto it(rhs.m_entries.begin()); it != rhs.m_entries.end(); ++it)
     321             :         {
     322        7785 :             m_entries.push_back((*it)->clone());
     323             :         }
     324             : 
     325          21 :         m_valid = rhs.m_valid;
     326             :     }
     327             : 
     328          21 :     return *this;
     329             : }
     330             : 
     331             : 
     332             : /** \brief Make sure the resources are released.
     333             :  *
     334             :  * The FileCollection destructor makes sure that any resources
     335             :  * still allocated get released.
     336             :  *
     337             :  * For example, the ZipFile implementation calls the close()
     338             :  * function.
     339             :  *
     340             :  * \note
     341             :  * Note that the entries generally get released when this
     342             :  * destructor is called. However, since we are using shared
     343             :  * pointers, you may still hold valid pointers to the entries
     344             :  * even after the FileCollection destructor was called.
     345             :  */
     346         696 : FileCollection::~FileCollection()
     347             : {
     348         696 : }
     349             : 
     350             : 
     351             : /** \brief Add an entry to this collection.
     352             :  *
     353             :  * This function adds an entry to the file collection allowing you to
     354             :  * create a FileCollection from the exact files you want to have in
     355             :  * the collection instead of having to read an entire directory as
     356             :  * the DirectoryCollection offers by default.
     357             :  *
     358             :  * \warning
     359             :  * This function creates a clone of the entry to make sure that
     360             :  * the caller's entry can be modified without affecting the
     361             :  * FileCollection.
     362             :  *
     363             :  * \param[in] entry  The entry to add to the FileCollection.
     364             :  */
     365       65734 : void FileCollection::addEntry(FileEntry const & entry)
     366             : {
     367       65734 :     m_entries.push_back(entry.clone());
     368       65734 : }
     369             : 
     370             : 
     371             : /** \brief Close the current FileEntry of this FileCollection.
     372             :  *
     373             :  * This function closes the current file entry.
     374             :  */
     375         686 : void FileCollection::close()
     376             : {
     377         686 :     m_entries.clear();
     378         686 :     m_filename = g_default_filename;
     379         686 :     m_valid = false;
     380         686 : }
     381             : 
     382             : 
     383             : /** \brief Retrieve the array of entries.
     384             :  *
     385             :  * This function returns a copy of the file collection vector of entries.
     386             :  * Note that the vector is copied but not the entries, so modifications
     387             :  * to the entries will be reflected in this FileCollection entries.
     388             :  * However, adding and removing entries to the collection is not
     389             :  * reflected in the copy.
     390             :  *
     391             :  * \return A vector containing the entries of this FileCollection.
     392             :  */
     393      198947 : FileEntry::vector_t FileCollection::entries() const
     394             : {
     395      198947 :     mustBeValid();
     396             : 
     397      198947 :     return m_entries;
     398             : }
     399             : 
     400             : 
     401             : /** \brief Get an entry from this collection.
     402             :  *
     403             :  * This function returns a shared pointer to a FileEntry object for
     404             :  * the entry with the specified name. To ignore the path part of the
     405             :  * filename while searching for a match, specify FileCollection::IGNORE
     406             :  * as the second argument.
     407             :  *
     408             :  * \note
     409             :  * The collection must be valid or the function raises an exception.
     410             :  *
     411             :  * \param[in] name  A string containing the name of the entry to get.
     412             :  * \param[in] matchpath  Specify MatchPath::MATCH, if the path should match
     413             :  *                       as well, specify MatchPath::IGNORE, if the path
     414             :  *                       should be ignored.
     415             :  *
     416             :  * \return A shared pointer to the found entry. The returned pointer
     417             :  *         is null if no entry is found.
     418             :  *
     419             :  * \sa mustBeValid()
     420             :  */
     421      197365 : FileEntry::pointer_t FileCollection::getEntry(std::string const & name, MatchPath matchpath) const
     422             : {
     423             :     // make sure the entries were loaded if necessary
     424      197365 :     entries();
     425             : 
     426      197365 :     mustBeValid();
     427             : 
     428      197365 :     FileEntry::vector_t::const_iterator iter;
     429      197365 :     if(matchpath == MatchPath::MATCH)
     430             :     {
     431      188022 :         iter = std::find_if(m_entries.begin(), m_entries.end(), MatchName(name));
     432             :     }
     433             :     else
     434             :     {
     435        9343 :         iter = std::find_if(m_entries.begin(), m_entries.end(), MatchFileName(name));
     436             :     }
     437             : 
     438      394730 :     return iter == m_entries.end() ? FileEntry::pointer_t() : *iter;
     439             : }
     440             : 
     441             : 
     442             : /** \brief Returns the name of the FileCollection.
     443             :  *
     444             :  * This function returns the filename of the collection as a whole.
     445             :  *
     446             :  * \note
     447             :  * The collection my be valid.
     448             :  *
     449             :  * \return The name of the FileCollection.
     450             :  *
     451             :  * \sa mustBeValid()
     452             :  */
     453         367 : std::string FileCollection::getName() const
     454             : {
     455         367 :     mustBeValid();
     456         341 :     return m_filename;
     457             : }
     458             : 
     459             : 
     460             : /** \brief Returns the number of entries in the FileCollection.
     461             :  *
     462             :  * This function returns the number of entries in the collection.
     463             :  *
     464             :  * \note
     465             :  * The collection my be valid.
     466             :  *
     467             :  * \return The number of entries in the FileCollection.
     468             :  *
     469             :  * \sa mustBeValid()
     470             :  */
     471         474 : size_t FileCollection::size() const
     472             : {
     473             :     // make sure the entries were loaded if necessary
     474         474 :     entries();
     475             : 
     476         455 :     mustBeValid();
     477         455 :     return m_entries.size();
     478             : }
     479             : 
     480             : 
     481             : /** \brief Check whether the current collection is valid.
     482             :  *
     483             :  * This function returns true if the collection is valid.
     484             :  *
     485             :  * Note that by default (just after a new) a collection is
     486             :  * not considered valid.
     487             :  *
     488             :  * \return true if the collection is valid.
     489             :  */
     490         386 : bool FileCollection::isValid() const
     491             : {
     492         386 :     return m_valid;
     493             : }
     494             : 
     495             : 
     496             : /** \brief Check whether the collection is valid.
     497             :  *
     498             :  * This function verifies that the collection is valid. If not, an
     499             :  * exception is raised. Many other functions from the various collection
     500             :  * functions are calling this function before accessing data.
     501             :  *
     502             :  * \exception InvalidStateException
     503             :  * This exception is raised if the m_valid field is currently false and
     504             :  * thus most of the collection data is considered invalid.
     505             :  */
     506      743601 : void FileCollection::mustBeValid() const
     507             : {
     508      743601 :     if(!m_valid)
     509             :     {
     510         223 :         throw InvalidStateException("Attempted to access an invalid FileCollection");
     511             :     }
     512      743378 : }
     513             : 
     514             : 
     515             : /** \brief Change the storage method to the specified value.
     516             :  *
     517             :  * This function changes the storage method of all the entries in
     518             :  * this collection to the specified value.
     519             :  *
     520             :  * The size limit is used to know which storage method to use:
     521             :  * small_storage_method for any file that has a size smaller or
     522             :  * equal to the specified limit and large_storage_method for the
     523             :  * others.
     524             :  *
     525             :  * \param[in] limit  The threshold to use to define the compression level.
     526             :  * \param[in] small_storage_method  The storage method for smaller files.
     527             :  * \param[in] large_storage_method  The storage method for larger files.
     528             :  *
     529             :  * \sa setLevel()
     530             :  */
     531         105 : void FileCollection::setMethod(
     532             :       std::size_t limit
     533             :     , StorageMethod small_storage_method
     534             :     , StorageMethod large_storage_method)
     535             : {
     536             :     // make sure the entries were loaded if necessary
     537         105 :     entries();
     538             : 
     539         105 :     mustBeValid();
     540             : 
     541       55954 :     for(auto it(m_entries.begin()); it != m_entries.end(); ++it)
     542             :     {
     543       55849 :         if((*it)->getSize() > limit)
     544             :         {
     545       49888 :             (*it)->setMethod(large_storage_method);
     546             :         }
     547             :         else
     548             :         {
     549        5961 :             (*it)->setMethod(small_storage_method);
     550             :         }
     551             :     }
     552         105 : }
     553             : 
     554             : 
     555             : /** \brief Change the compression level to the specified value.
     556             :  *
     557             :  * This function changes the compression level of all the entries in
     558             :  * this collection to the specified value.
     559             :  *
     560             :  * The size limit is used to know which compression level to use:
     561             :  * small_compression_level for any file that has a size smaller or
     562             :  * equal to the specified limit and large_compression_level for the
     563             :  * others.
     564             :  *
     565             :  * \param[in] limit  The threshold to use to define the compression level.
     566             :  * \param[in] small_compression_level  The compression level for smaller files.
     567             :  * \param[in] large_compression_level  The compression level for larger files.
     568             :  *
     569             :  * \sa setMethod()
     570             :  */
     571         105 : void FileCollection::setLevel(
     572             :       std::size_t limit
     573             :     , FileEntry::CompressionLevel small_compression_level
     574             :     , FileEntry::CompressionLevel large_compression_level)
     575             : {
     576             :     // make sure the entries were loaded if necessary
     577         105 :     entries();
     578             : 
     579         105 :     mustBeValid();
     580             : 
     581       55954 :     for(auto it(m_entries.begin()); it != m_entries.end(); ++it)
     582             :     {
     583       55849 :         if((*it)->getSize() > limit)
     584             :         {
     585       49888 :             (*it)->setLevel(large_compression_level);
     586             :         }
     587             :         else
     588             :         {
     589        5961 :             (*it)->setLevel(small_compression_level);
     590             :         }
     591             :     }
     592         105 : }
     593             : 
     594             : 
     595             : /** \brief Write a FileCollection to the output stream.
     596             :  *
     597             :  * This function writes a simple textual representation of this
     598             :  * FileCollection to the output stream.
     599             :  *
     600             :  * \param[in,out] os  The output stream.
     601             :  * \param[in] collection  The collection to print out.
     602             :  *
     603             :  * \return A reference to the \p os output stream.
     604             :  */
     605           6 : std::ostream & operator << (std::ostream & os, FileCollection const & collection)
     606             : {
     607           6 :     os << "collection '" << collection.getName() << "' {";
     608           6 :     FileEntry::vector_t entries(collection.entries());
     609           6 :     char const *sep("");
     610        7145 :     for(auto it = entries.begin(); it != entries.end(); ++it)
     611             :     {
     612        7139 :         os << sep;
     613        7139 :         sep = ", ";
     614        7139 :         os << (*it)->getName();
     615             :     }
     616           6 :     os << "}";
     617           6 :     return os;
     618           6 : }
     619             : 
     620             : 
     621             : } // zipios namespace
     622             : 
     623             : // Local Variables:
     624             : // mode: cpp
     625             : // indent-tabs-mode: nil
     626             : // c-basic-offset: 4
     627             : // tab-width: 4
     628             : // End:
     629             : 
     630             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions