LCOV - code coverage report
Current view: top level - include/ioutils/fits - fitsHeader.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 100.0 % 259 259
Test Date: 2026-08-30 22:23:37 Functions: 100.0 % 30 30

            Line data    Source code
       1              : /** \file fitsHeader.hpp
       2              :  * \brief Declares and defines a class to work with a FITS header
       3              :  * \ingroup fits_processing_files
       4              :  *
       5              :  */
       6              : 
       7              : //***********************************************************************//
       8              : // Copyright 2015-2025 Jared R. Males (jaredmales@gmail.com)
       9              : //
      10              : // This file is part of mxlib.
      11              : //
      12              : // mxlib is free software: you can redistribute it and/or modify
      13              : // it under the terms of the GNU General Public License as published by
      14              : // the Free Software Foundation, either version 3 of the License, or
      15              : // (at your option) any later version.
      16              : //
      17              : // mxlib is distributed in the hope that it will be useful,
      18              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      19              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      20              : // GNU General Public License for more details.
      21              : //
      22              : // You should have received a copy of the GNU General Public License
      23              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      24              : //***********************************************************************//
      25              : 
      26              : #ifndef ioutils_fits__fitsHeader_hpp
      27              : #define ioutils_fits__fitsHeader_hpp
      28              : 
      29              : #include <list>
      30              : #include <unordered_map>
      31              : #include <iostream>
      32              : #include <vector>
      33              : #include <optional>
      34              : 
      35              : #include "fitsHeaderCard.hpp"
      36              : 
      37              : namespace mx
      38              : {
      39              : namespace fits
      40              : {
      41              : 
      42              : namespace fitsHeaderDetail
      43              : {
      44              : 
      45              : /** \cond */
      46              : /// Mutation stages exposed to deterministic failure tests.
      47              : enum class mutation
      48              : {
      49              :     appendList,
      50              :     appendMap,
      51              :     insertBeforeList,
      52              :     insertBeforeMap,
      53              :     insertAfterList,
      54              :     insertAfterMap
      55              : };
      56              : 
      57              : /// Signature of the resettable mutation hook used by fitsHeader.
      58              : using mutationHookT = error_t ( * )( mutation );
      59              : 
      60              : /// Access the process-wide fitsHeader mutation hook.
      61              : mutationHookT &mutationHook();
      62              : 
      63              : /// Restore the fitsHeader mutation hook to its production no-op.
      64              : void resetMutationHook();
      65              : /** \endcond */
      66              : 
      67              : } // namespace fitsHeaderDetail
      68              : 
      69              : /// Class to manage a FITS file metadata header and provide fast access to the cards by keyword
      70              : /** Manages tasks such as insertion (avoiding duplicates), and keyword lookup.
      71              :  *
      72              :  * The \ref fitsHeaderCard "cards" are stored in a `std::list` to preserve order, but
      73              :  * a `std::unordered_multimap` is used to provide fast keyword lookup into the list.
      74              :  *
      75              :  * \tparam verboseT sets the error reporting \ref mx::verbose "verbosity"
      76              :  *
      77              :  * \ingroup fits_processing
      78              :  */
      79              : template <class verboseT = verbose::d>
      80              : class fitsHeader
      81              : {
      82              :   public:
      83              :     /// The list type
      84              :     /** We use a list,  rather than forward_list, so that append (insert at end) is constant time.
      85              :      *
      86              :      */
      87              :     typedef std::list<fitsHeaderCard<verboseT>> cardListT;
      88              : 
      89              :     /// The iterator type for the cards list
      90              :     typedef cardListT::iterator headerIteratorT;
      91              : 
      92              :     /// The map type
      93              :     /** Use unordered_multimap to allow multiple HISTORY and COMMENT properly, but be as efficient as possible.
      94              :      */
      95              :     typedef std::unordered_multimap<std::string, headerIteratorT> cardMapT;
      96              : 
      97              :     /// The value type for the card map
      98              :     typedef cardMapT::value_type cardMapValueT;
      99              : 
     100              :     /// The iterator type for the card map
     101              :     typedef cardMapT::iterator mapIteratorT;
     102              : 
     103              :   protected:
     104              :     /// The storage for the FITS header cards
     105              :     cardListT m_cardList;
     106              : 
     107              :     /// This multimap allows for fast lookup by keyword.
     108              :     cardMapT m_cardMap;
     109              : 
     110              :     /// Card with empty keyword used as an error sentinel.
     111              :     mutable fitsHeaderCard<verboseT> m_emptyCard;
     112              : 
     113              :   public:
     114              :     /// Default c'tor
     115              :     fitsHeader();
     116              : 
     117              :     /// Copy constructor
     118              :     fitsHeader( const fitsHeader &head /**< The fitsHeader to copy */ );
     119              : 
     120              :     /// Destructor
     121              :     ~fitsHeader();
     122              : 
     123              :     /// Assignment operator
     124              :     fitsHeader &operator=( const fitsHeader &head /**< The fitsHeader to copy */ );
     125              : 
     126              :     /// Get iterator to the beginning of the cards list
     127              :     headerIteratorT begin();
     128              : 
     129              :     /// Get iterator to the end of the cards list
     130              :     headerIteratorT end();
     131              : 
     132              :     /// Get iterator pointing to a specific element
     133              :     headerIteratorT iterator( const std::string &keyword /**< The keyword to look up*/ );
     134              : 
     135              :     /// Test whether the header is empty.
     136              :     bool empty();
     137              : 
     138              :     /// Get number of cards currently stored in the header.
     139              :     size_t size();
     140              : 
     141              :     /// Clear all cards from the header
     142              :     error_t clear();
     143              : 
     144              :     /// Get number of cards with a given keyword
     145              :     /** Returns the result of the count() method of the header map.
     146              :      *
     147              :      * \retval the number of cards with  keyword.
     148              :      */
     149              :     size_t count( const std::string &keyword /**< [in] the keyword to look up*/ );
     150              : 
     151              :     /// Erase card by keyword
     152              :     /** This can not be used to erase COMMENT or HISTORY cards.
     153              :      *
     154              :      */
     155              :     error_t erase( const std::string &keyword /**< [in] the keyword of the card to delete*/ );
     156              : 
     157              :     /// Erase card by iterator
     158              :     /** This handles COMMENT and HISTORY cards, deleting only the one pointed to by it
     159              :      *  using all the contents of the card (not just the keyword)
     160              :      */
     161              :     error_t erase( headerIteratorT it /**< [in] iterator pointing to the card to delete. */ );
     162              : 
     163              :     /// Erase the standard entries at the top of the header
     164              :     /** Erases each entry down to BSCALE.  This is useful for appending
     165              :      * a header from a previous image to a newly created file. Also erases boilerplate comments,
     166              :      * such as for long string.
     167              :      */
     168              :     error_t eraseStandardTop();
     169              : 
     170              :     /// Append a fitsHeaderCard to the end of the header
     171              :     /**
     172              :      */
     173              :     error_t append( const fitsHeaderCard<verboseT> &card /**< [in] the card to append*/ );
     174              : 
     175              :     /// Append a string card to the end of the header, from the three components of a card.
     176              :     /**
     177              :      */
     178              :     error_t append( const std::string &k, /**< [in] the keyword of the new card*/
     179              :                     const char *v,        /**< [in] the value of the new card*/
     180              :                     const std::string &c  /**< [in] the comment of the new card*/
     181              :     );
     182              : 
     183              :     /// Append a card to the end of the header, from the three components of a card.
     184              :     /**
     185              :      * \tparam typeT is the data type of the value
     186              :      *
     187              :      */
     188              :     template <typename typeT>
     189              :     error_t append( const std::string &k, /**< [in] the keyword of the new card*/
     190              :                     const typeT &v,       /**< [in] the value of the new card*/
     191              :                     const std::string &c  /**< [in] the comment of the new card*/
     192              :     );
     193              : 
     194              :     /// Append a card to the end of the header, from the components of a card with no comment.
     195              :     /**
     196              :      * \tparam typeT is the data type of the value
     197              :      *
     198              :      */
     199              :     template <typename typeT>
     200              :     error_t append( const std::string &k, /**< [in] the keyword of the new card*/
     201              :                     const typeT &v        /**< [in] the value of the new card*/
     202              :     );
     203              : 
     204              :     /// Append a card to the end of the header, with just a keyword.
     205              :     /** Appends a headerCard with unknownType
     206              :      *
     207              :      */
     208              :     error_t append( const std::string &k /**< [in] the keyword of the new card*/ );
     209              : 
     210              :     /// Append a fitsHeader to the end of the header
     211              :     /**
     212              :      */
     213              :     error_t append( fitsHeader &head /**< [in]  the fitsHeader to append*/ );
     214              : 
     215              :     /// Insert a card before another card.
     216              :     /**
     217              :      */
     218              :     error_t insert_before( headerIteratorT it,           /**< [in] iterator pointing to the
     219              :                                                                    element before which to insert*/
     220              :                            fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
     221              :     );
     222              : 
     223              :     /// Insert a card before another card, specifying the card by its components.
     224              :     /**
     225              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     226              :      *
     227              :      */
     228              :     template <typename typeT>
     229              :     error_t insert_before( headerIteratorT it,   /**< [in] iterator pointing to the element before which to insert*/
     230              :                            const std::string &k, /**< [in] the keyword of the new card*/
     231              :                            typeT v,              /**< [in] the value of the new card*/
     232              :                            const std::string &c  /**< [in] the comment of the new card*/
     233              :     );
     234              : 
     235              :     /// Insert a card before another card, specifying the card by its components.
     236              :     /**
     237              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     238              :      *
     239              :      */
     240              :     template <typename typeT>
     241              :     error_t insert_before( headerIteratorT it,   /**< [in] iterator pointing to the element before which to insert*/
     242              :                            const std::string &k, /**< [in] the keyword of the new card*/
     243              :                            typeT v               /**< [in] the value of the new card*/
     244              :     );
     245              : 
     246              :     /// Insert a card after another card.
     247              :     /**
     248              :      */
     249              :     error_t insert_after( headerIteratorT it,           /**< [in] iterator pointing to the element
     250              :                                                                   after which to insert*/
     251              :                           fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
     252              :     );
     253              : 
     254              :     /// Insert a card after another card, specifying the card by its components.
     255              :     /**
     256              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     257              :      *
     258              :      */
     259              :     template <typename typeT>
     260              :     error_t insert_after( headerIteratorT it,   /**< [in] iterator pointing to the element after which to insert*/
     261              :                           const std::string &k, /**< [in] the keyword of the new card*/
     262              :                           typeT v,              /**< [in] the value of the new card*/
     263              :                           const std::string &c  /**< [in] the comment of the new card*/
     264              :     );
     265              : 
     266              :     /// Insert a card after another card, specifying the card by its components.
     267              :     /**
     268              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     269              :      *
     270              :      */
     271              :     template <typename typeT>
     272              :     error_t insert_after( headerIteratorT it,   /**< [in] interator pointing to the element after which to insert*/
     273              :                           const std::string &k, /**< [in] the keyword of the new card*/
     274              :                           typeT v               /**< [in] the value of the new card*/
     275              :     );
     276              : 
     277              :     /// Card access by keyword operator
     278              :     /** Looks up the card by its keyword, and returns a reference to it.
     279              :      *
     280              :      * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
     281              :      * \returns on error: a fitsHeaderCard& to a card with an empty keyword
     282              :      */
     283              :     fitsHeaderCard<verboseT> &operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ );
     284              : 
     285              :     /// Card access by keyword operator (const version)
     286              :     /** Looks up the card by its keyword, and returns a reference to it.
     287              :      *
     288              :      * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
     289              :      * \returns on error: a fitsHeaderCard& to a card with an empty keyword
     290              :      */
     291              :     const fitsHeaderCard<verboseT> &
     292              :     operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ ) const;
     293              : 
     294              : }; // fitsHeader
     295              : 
     296              : template <class verboseT>
     297          113 : fitsHeader<verboseT>::fitsHeader()
     298              : {
     299          113 : }
     300              : 
     301              : template <class verboseT>
     302            2 : fitsHeader<verboseT>::fitsHeader( const fitsHeader &head )
     303              : {
     304            2 :     operator=( head );
     305            2 : }
     306              : 
     307              : template <class verboseT>
     308          115 : fitsHeader<verboseT>::~fitsHeader()
     309              : {
     310          115 :     clear();
     311          115 : }
     312              : 
     313              : template <class verboseT>
     314            4 : fitsHeader<verboseT> &fitsHeader<verboseT>::operator=( const fitsHeader &head )
     315              : {
     316            4 :     m_cardList = head.m_cardList;
     317              : 
     318            4 :     headerIteratorT it = m_cardList.begin();
     319              : 
     320            4 :     m_cardMap.clear();
     321           19 :     while( it != m_cardList.end() )
     322              :     {
     323           15 :         m_cardMap.insert( cardMapValueT( it->keyword(), it ) );
     324           15 :         ++it;
     325              :     }
     326              : 
     327            4 :     return *this;
     328              : }
     329              : 
     330              : template <class verboseT>
     331           96 : fitsHeader<verboseT>::headerIteratorT fitsHeader<verboseT>::begin()
     332              : {
     333           96 :     return m_cardList.begin();
     334              : }
     335              : 
     336              : template <class verboseT>
     337          226 : fitsHeader<verboseT>::headerIteratorT fitsHeader<verboseT>::end()
     338              : {
     339          226 :     return m_cardList.end();
     340              : }
     341              : 
     342              : template <class verboseT>
     343            9 : fitsHeader<verboseT>::headerIteratorT fitsHeader<verboseT>::iterator( const std::string &keyword )
     344              : {
     345            9 :     auto mit = m_cardMap.find( keyword );
     346            9 :     if( mit == m_cardMap.end() )
     347              :     {
     348            2 :         return m_cardList.end();
     349              :     }
     350              : 
     351            7 :     return mit->second;
     352              : }
     353              : 
     354              : template <class verboseT>
     355           11 : bool fitsHeader<verboseT>::empty()
     356              : {
     357           11 :     return m_cardList.empty();
     358              : }
     359              : 
     360              : template <class verboseT>
     361           47 : size_t fitsHeader<verboseT>::size()
     362              : {
     363           47 :     return m_cardList.size();
     364              : }
     365              : 
     366              : template <class verboseT>
     367          117 : error_t fitsHeader<verboseT>::clear()
     368              : {
     369          117 :     m_cardList.clear();
     370          117 :     m_cardMap.clear();
     371              : 
     372          117 :     return error_t::noerror;
     373              : }
     374              : 
     375              : template <class verboseT>
     376           28 : size_t fitsHeader<verboseT>::count( const std::string &keyword )
     377              : {
     378           28 :     return m_cardMap.count( keyword );
     379              : }
     380              : 
     381              : template <class verboseT>
     382            4 : error_t fitsHeader<verboseT>::erase( const std::string &keyword )
     383              : {
     384            4 :     if( keyword == "COMMENT" )
     385              :     {
     386            3 :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase COMMENT by keyword" );
     387              :     }
     388              : 
     389            3 :     if( keyword == "HISTORY" )
     390              :     {
     391            3 :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase HISTORY by keyword" );
     392              :     }
     393              : 
     394            2 :     auto mit = m_cardMap.find( keyword );
     395            2 :     if( mit == m_cardMap.end() )
     396              :     {
     397            1 :         return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in map:" + keyword );
     398              :     }
     399              : 
     400            1 :     headerIteratorT it = mit->second;
     401              : 
     402            1 :     m_cardMap.erase( mit );
     403            1 :     m_cardList.erase( it );
     404              : 
     405            1 :     return error_t::noerror;
     406              : }
     407              : 
     408              : template <class verboseT>
     409           15 : error_t fitsHeader<verboseT>::erase( headerIteratorT it )
     410              : {
     411           15 :     if( it == m_cardList.end() )
     412              :     {
     413            3 :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid list iterator" );
     414              :     }
     415              : 
     416           14 :     std::string keyword = it->keyword();
     417              : 
     418           14 :     auto range = m_cardMap.equal_range( keyword );
     419           14 :     mapIteratorT mit;
     420           15 :     for( mit = range.first; mit != range.second; ++mit )
     421              :     {
     422           15 :         if( mit->second == it )
     423              :         {
     424           14 :             break;
     425              :         }
     426              :     }
     427              : 
     428           14 :     m_cardMap.erase( mit ); // does not throw
     429           14 :     m_cardList.erase( it );
     430              : 
     431           14 :     return error_t::noerror;
     432           14 : }
     433              : 
     434              : template <class verboseT>
     435            1 : error_t fitsHeader<verboseT>::eraseStandardTop()
     436              : {
     437              : 
     438            1 :     headerIteratorT it = begin(), nit;
     439              : 
     440            1 :     int n = 0;
     441           15 :     while( it != end() )
     442              :     {
     443           15 :         nit = it;
     444           15 :         ++nit;
     445           29 :         if( it->keyword() == "SIMPLE" || it->keyword() == "BITPIX" || it->keyword() == "NAXIS" ||
     446           12 :             it->keyword() == "NAXIS1" || it->keyword() == "NAXIS2" || it->keyword() == "NAXIS3" ||
     447           35 :             it->keyword() == "EXTEND" || it->keyword() == "BZERO" || it->keyword() == "BSCALE" ||
     448            6 :             it->keyword() == "LONGSTRN" )
     449              :         {
     450           10 :             mxlib_error_check( erase( it ) );
     451              :         }
     452              : 
     453           15 :         if( it->keyword() == "COMMENT" )
     454              :         {
     455            3 :             if( it->comment().find( "FITS (Flexible Image" ) != std::string::npos )
     456              :             {
     457            1 :                 mxlib_error_check( erase( it ) );
     458              :             }
     459            2 :             else if( it->comment().find( "and Astrophysics'" ) != std::string::npos )
     460              :             {
     461            1 :                 mxlib_error_check( erase( it ) );
     462              :             }
     463              :         }
     464              : 
     465           15 :         if( nit == end() )
     466              :         {
     467            1 :             break;
     468              :         }
     469           14 :         it = nit;
     470           14 :         ++n;
     471              :     }
     472              : 
     473            1 :     return error_t::noerror;
     474              : }
     475              : 
     476              : template <class verboseT>
     477          274 : error_t fitsHeader<verboseT>::append( const fitsHeaderCard<verboseT> &card )
     478              : {
     479          274 :     if( card.keyword() == "CONTINUE" )
     480              :     {
     481            2 :         if( m_cardList.empty() )
     482              :         {
     483            2 :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     484            3 :                                                            "CONTINUE card requires a preceding card" );
     485              :         }
     486              : 
     487            1 :         headerIteratorT backIt = m_cardList.end();
     488            1 :         --backIt;
     489            1 :         return backIt->appendContinue( card );
     490              :     }
     491              : 
     492              :     // First check if duplicate key
     493          272 :     if( m_cardMap.count( card.keyword() ) > 0 )
     494              :     {
     495           22 :         if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
     496              :         {
     497            4 :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     498            8 :                                                            "attempt to duplicate keyword " + card.keyword() );
     499              :         }
     500              :     }
     501              : 
     502          268 :     headerIteratorT insertedIt;
     503              : 
     504              :     // Now insert in list
     505              :     try
     506              :     {
     507          268 :         error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::appendList );
     508          266 :         if( errc != error_t::noerror )
     509              :         {
     510            2 :             return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
     511              :         }
     512              : 
     513          264 :         m_cardList.push_back( card );
     514          264 :         insertedIt = m_cardList.end();
     515          264 :         --insertedIt;
     516              :     }
     517            3 :     catch( const std::bad_alloc &e )
     518              :     {
     519            1 :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     520            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     521              :         // clang-format off
     522              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     523              :             return error_t::std_bad_alloc;
     524              :         #else
     525            1 :             throw;
     526              :         #endif
     527              :         // clang-format on
     528              :     }
     529            2 :     catch( const std::exception &e )
     530              :     {
     531            1 :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     532            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     533              :         // clang-format off
     534              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     535              :             return error_t::std_exception;
     536              :         #else
     537            1 :             throw;
     538              :         #endif
     539              :         // clang-format on
     540              :     }
     541              : 
     542              :     // Then add to the Map.
     543              :     try
     544              :     {
     545          264 :         error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::appendMap );
     546          262 :         if( errc != error_t::noerror )
     547              :         {
     548            1 :             m_cardList.erase( insertedIt );
     549            1 :             return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
     550              :         }
     551              : 
     552          261 :         m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
     553              :     }
     554            3 :     catch( const std::bad_alloc &e )
     555              :     {
     556            1 :         m_cardList.erase( insertedIt );
     557            1 :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     558            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     559              :         // clang-format off
     560              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     561              :             return error_t::std_bad_alloc;
     562              :         #else
     563            1 :             throw;
     564              :         #endif
     565              :         // clang-format on
     566              :     }
     567            2 :     catch( const std::exception &e )
     568              :     {
     569            1 :         m_cardList.erase( insertedIt );
     570            1 :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     571            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     572              :         // clang-format off
     573              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     574              :             return error_t::std_exception;
     575              :         #else
     576            1 :             throw;
     577              :         #endif
     578              :         // clang-format on
     579              :     }
     580              : 
     581          261 :     return error_t::noerror;
     582              : }
     583              : 
     584              : template <class verboseT>
     585            5 : error_t fitsHeader<verboseT>::append( fitsHeader &head )
     586              : {
     587            5 :     if( this == &head )
     588              :     {
     589            1 :         fitsHeader copy( head );
     590            1 :         return append( copy );
     591            1 :     }
     592              : 
     593            4 :     headerIteratorT it;
     594              : 
     595            7 :     for( it = head.begin(); it != head.end(); ++it )
     596              :     {
     597            5 :         mxlib_error_check( append( *it ) );
     598              :     }
     599              : 
     600            2 :     return error_t::noerror;
     601              : }
     602              : 
     603              : template <class verboseT>
     604           29 : error_t fitsHeader<verboseT>::append( const std::string &k )
     605              : {
     606           29 :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k ) ) );
     607              : }
     608              : 
     609              : template <class verboseT>
     610            5 : error_t fitsHeader<verboseT>::append( const std::string &k, const char *v, const std::string &c )
     611              : {
     612            5 :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, c ) ) );
     613              : }
     614              : 
     615              : template <class verboseT>
     616              : template <typename typeT>
     617          191 : error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v, const std::string &c )
     618              : {
     619          191 :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, c ) ) );
     620              : }
     621              : 
     622              : template <class verboseT>
     623              : template <typename typeT>
     624           41 : error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v )
     625              : {
     626           86 :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, "" ) ) );
     627              : }
     628              : 
     629              : template <class verboseT>
     630           10 : error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, fitsHeaderCard<verboseT> card )
     631              : {
     632              :     // First check if duplicate key
     633           10 :     if( m_cardMap.count( card.keyword() ) > 0 )
     634              :     {
     635            1 :         if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
     636              :         {
     637            1 :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     638            2 :                                                            "duplicate keyword: " + card.keyword() );
     639              :         }
     640              :     }
     641              : 
     642              :     // Now insert in list
     643            9 :     headerIteratorT insertedIt;
     644              : 
     645              :     try
     646              :     {
     647            9 :         error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertBeforeList );
     648            7 :         if( errc != error_t::noerror )
     649              :         {
     650            1 :             return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
     651              :         }
     652              : 
     653            6 :         insertedIt = m_cardList.insert( it, card );
     654              :     }
     655            3 :     catch( const std::bad_alloc &e )
     656              :     {
     657            1 :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     658            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     659              :         // clang-format off
     660              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     661              :             return error_t::std_bad_alloc;
     662              :         #else
     663            1 :             throw;
     664              :         #endif
     665              :         // clang-format on
     666              :     }
     667            2 :     catch( const std::exception &e )
     668              :     {
     669            1 :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     670            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     671              :         // clang-format off
     672              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     673              :             return error_t::std_exception;
     674              :         #else
     675            1 :             throw;
     676              :         #endif
     677              :         // clang-format on
     678              :     }
     679              : 
     680              :     // Then add to the Map.
     681              :     try
     682              :     {
     683            6 :         error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertBeforeMap );
     684            4 :         if( errc != error_t::noerror )
     685              :         {
     686            1 :             m_cardList.erase( insertedIt );
     687            1 :             return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
     688              :         }
     689              : 
     690            3 :         m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
     691              :     }
     692            3 :     catch( const std::bad_alloc &e )
     693              :     {
     694            1 :         m_cardList.erase( insertedIt );
     695            1 :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     696            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     697              :         // clang-format off
     698              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     699              :             return error_t::std_bad_alloc;
     700              :         #else
     701            1 :             throw;
     702              :         #endif
     703              :         // clang-format on
     704              :     }
     705            2 :     catch( const std::exception &e )
     706              :     {
     707            1 :         m_cardList.erase( insertedIt );
     708            1 :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     709            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     710              :         // clang-format off
     711              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     712              :             return error_t::std_exception;
     713              :         #else
     714            1 :             throw;
     715              :         #endif
     716              :         // clang-format on
     717              :     }
     718              : 
     719            3 :     return error_t::noerror;
     720              : }
     721              : 
     722              : template <class verboseT>
     723              : template <typename typeT>
     724            1 : error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
     725              : {
     726            1 :     mxlib_error_return( insert_before( it, fitsHeaderCard<verboseT>( k, v, c ) ) );
     727              : }
     728              : 
     729              : template <class verboseT>
     730              : template <typename typeT>
     731            7 : error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, const std::string &k, typeT v )
     732              : {
     733           18 :     mxlib_error_return( insert_before( it, fitsHeaderCard<verboseT>( k, v, "" ) ) );
     734              : }
     735              : 
     736              : template <class verboseT>
     737              : template <typename typeT>
     738            1 : error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
     739              : {
     740            1 :     mxlib_error_return( insert_after( it, fitsHeaderCard<verboseT>( k, v, c ) ) );
     741              : }
     742              : 
     743              : template <class verboseT>
     744           10 : error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, fitsHeaderCard<verboseT> card )
     745              : {
     746           10 :     if( it == m_cardList.end() )
     747              :     {
     748            3 :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid list iterator" );
     749              :     }
     750              : 
     751              :     // First check if duplicate key
     752            9 :     if( m_cardMap.count( card.keyword() ) > 0 )
     753              :     {
     754            2 :         if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
     755              :         {
     756            1 :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     757            2 :                                                            "duplicate keyword: " + card.keyword() );
     758              :         }
     759              :     }
     760              : 
     761              :     // Now insert in list
     762            8 :     headerIteratorT insertedIt;
     763              : 
     764              :     try
     765              :     {
     766            8 :         error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertAfterList );
     767            6 :         if( errc != error_t::noerror )
     768              :         {
     769            1 :             return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
     770              :         }
     771              : 
     772            5 :         insertedIt = m_cardList.insert( ++it, card );
     773              :     }
     774            3 :     catch( const std::bad_alloc &e )
     775              :     {
     776            1 :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     777            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     778              :         // clang-format off
     779              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     780              :             return error_t::std_bad_alloc;
     781              :         #else
     782            1 :             throw;
     783              :         #endif
     784              :         // clang-format on
     785              :     }
     786            2 :     catch( const std::exception &e )
     787              :     {
     788            1 :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     789            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     790              :         // clang-format off
     791              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     792              :             return error_t::std_exception;
     793              :         #else
     794            1 :             throw;
     795              :         #endif
     796              :         // clang-format on
     797              :     }
     798              : 
     799              :     // Then add to the Map.
     800              :     try
     801              :     {
     802            5 :         error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertAfterMap );
     803            3 :         if( errc != error_t::noerror )
     804              :         {
     805            1 :             m_cardList.erase( insertedIt );
     806            1 :             return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
     807              :         }
     808              : 
     809            2 :         m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
     810              :     }
     811            3 :     catch( const std::bad_alloc &e )
     812              :     {
     813            1 :         m_cardList.erase( insertedIt );
     814            1 :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     815            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     816              :         // clang-format off
     817              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     818              :             return error_t::std_bad_alloc;
     819              :         #else
     820            1 :             throw;
     821              :         #endif
     822              :         // clang-format on
     823              :     }
     824            2 :     catch( const std::exception &e )
     825              :     {
     826            1 :         m_cardList.erase( insertedIt );
     827            1 :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     828            2 :                                                 "inserting " + card.keyword() + " :" + e.what() );
     829              :         // clang-format off
     830              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     831              :             return error_t::std_exception;
     832              :         #else
     833            1 :             throw;
     834              :         #endif
     835              :         // clang-format on
     836              :     }
     837              : 
     838            2 :     return error_t::noerror;
     839              : }
     840              : 
     841              : template <class verboseT>
     842              : template <typename typeT>
     843            8 : error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, const std::string &k, typeT v )
     844              : {
     845           20 :     mxlib_error_return( insert_after( it, fitsHeaderCard<verboseT>( k, v, "" ) ) );
     846              : }
     847              : 
     848              : template <class verboseT>
     849          180 : fitsHeaderCard<verboseT> &fitsHeader<verboseT>::operator[]( const std::string &keyword )
     850              : {
     851          180 :     headerIteratorT it;
     852              : 
     853          180 :     auto mit = m_cardMap.find( keyword );
     854              : 
     855              :     // If not found, append it.
     856          180 :     if( mit == m_cardMap.end() )
     857              :     {
     858           12 :         error_t errc = append( keyword );
     859           12 :         if( errc != error_t::noerror )
     860              :         {
     861            1 :             internal::mxlib_error_report<verboseT>( errc, "appending " + keyword );
     862            2 :             m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
     863            1 :             return m_emptyCard;
     864              :         }
     865              : 
     866              :         // have to do new search
     867           11 :         mit = m_cardMap.find( keyword );
     868              :     }
     869              : 
     870          179 :     it = mit->second;
     871              : 
     872          179 :     return *it;
     873              : }
     874              : 
     875              : template <class verboseT>
     876            3 : const fitsHeaderCard<verboseT> &fitsHeader<verboseT>::operator[]( const std::string &keyword ) const
     877              : {
     878            3 :     auto mit = m_cardMap.find( keyword );
     879            3 :     if( mit == m_cardMap.end() )
     880              :     {
     881            2 :         internal::mxlib_error_report<verboseT>( error_t::notfound, keyword + " not found" );
     882            4 :         m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
     883            2 :         return m_emptyCard;
     884              :     }
     885              : 
     886            1 :     headerIteratorT it = mit->second;
     887              : 
     888            1 :     return *it;
     889              : }
     890              : 
     891              : /** \addtogroup fits_utils
     892              :  * @{
     893              :  */
     894              : 
     895              : /// Convert the values in a std::vector of \ref fitsHeader "fits headers" into a std::vector of values.
     896              : /** Resizes the vector of the appropriate type.
     897              :  *
     898              :  * \returns error_t::noerror on success. \p bad will be empty.
     899              :  * \returns error_t::error if the keyword fails to convert for any header.  \p bad will contain the indices of
     900              :  *          \p heads for which the extraction of a value for \p keyw failed
     901              :  *
     902              :  * \tparam dataT is the type of the header value
     903              :  * \tparam fitsHeaderT is the fitsHeader type
     904              :  *
     905              :  */
     906              : template <typename dataT, class fitsHeaderT>
     907            2 : error_t headersToValues( std::vector<dataT> &v,           /**< [out] will contain the converted values*/
     908              :                          std::vector<size_t> &bad,        /**<[out] will contain the indices of any
     909              :                                                                     headers that failed conversion */
     910              :                          std::vector<fitsHeaderT> &heads, /**< [in] contains the headers */
     911              :                          const std::string &keyw          /**< [in] contains the keyword designating which
     912              :                                                                     value to convert*/
     913              : )
     914              : {
     915            2 :     v.resize( heads.size() );
     916            2 :     bad.clear();
     917              : 
     918           12 :     for( size_t i = 0; i < heads.size(); ++i )
     919              :     {
     920              :         error_t errc;
     921           10 :         v[i] = heads[i][keyw].template value<dataT>( &errc ); // convertFromString<dataT>(heads[i][keyw].value);
     922              : 
     923           10 :         if( errc != error_t::noerror )
     924              :         {
     925            1 :             bad.push_back( i );
     926            1 :             v[i] = std::numeric_limits<dataT>::max();
     927              :         }
     928              :     }
     929              : 
     930            2 :     if( bad.size() > 0 )
     931              :     {
     932            1 :         return error_t::error;
     933              :     }
     934              :     else
     935              :     {
     936            1 :         return error_t::noerror;
     937              :     }
     938              : }
     939              : 
     940              : /// Write the status of a Git repository to HISTORY in a FITS header.
     941              : /**
     942              :  * \param [in,out] head the HISTORY cards will be appended to this header
     943              :  * \param [in] repoName the name of the repository
     944              :  * \param [in] sha1 is the SHA-1 hash string of the repository
     945              :  * \param [in] modified whether or not the repository has been modified after the
     946              :  *                      commit referred to by sha1
     947              :  */
     948              : template <class fitsHeaderT>
     949            2 : void fitsHeaderGitStatus( fitsHeaderT &head, const std::string &repoName, const char *sha1, int modified )
     950              : {
     951            2 :     std::string hist = "Git status for " + repoName + ":";
     952            4 :     head.append( "", fitsHistoryType(), hist );
     953              : 
     954            2 :     hist = "   sha1=";
     955            2 :     hist += sha1;
     956            2 :     if( modified )
     957            1 :         hist += ", modified";
     958              : 
     959            4 :     head.append( "", fitsHistoryType(), hist );
     960            2 : }
     961              : 
     962              : ///@}
     963              : 
     964              : extern template class fitsHeader<verbose::d>;
     965              : 
     966              : } // namespace fits
     967              : } // namespace mx
     968              : 
     969              : #endif // ioutils_fits__fitsHeader_hpp
        

Generated by: LCOV version 2.0-1