Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / RepoStatus.cc
index 059474f..ba10fc7 100644 (file)
 #include <iostream>
 #include <sstream>
 #include <fstream>
+#include <set>
 #include <zypp/base/Logger.h>
 #include <zypp/base/String.h>
 #include <zypp/RepoStatus.h>
+#include <zypp/RepoInfo.h>
 #include <zypp/PathInfo.h>
 
 using std::endl;
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  {
+    /** Recursive computation of max dir timestamp. */
+    void recursiveTimestamp( const Pathname & dir_r, time_t & max_r )
+    {
+      std::list<std::string> dircontent;
+      if ( filesystem::readdir( dircontent, dir_r, false/*no dots*/ ) != 0 )
+       return; // readdir logged the error
+
+      for_( it, dircontent.begin(), dircontent.end() )
+      {
+       PathInfo pi( dir_r + *it, PathInfo::LSTAT );
+       if ( pi.isDir() )
+       {
+         if ( pi.mtime() > max_r )
+           max_r = pi.mtime();
+         recursiveTimestamp( pi.path(), max_r );
+       }
+      }
+    }
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
   //
@@ -30,44 +55,89 @@ namespace zypp
   /** RepoStatus implementation. */
   struct RepoStatus::Impl
   {
-  public:
-    std::string _checksum;
-    Date _timestamp;
+    using Checksums = std::set<std::string>;
 
-    // NOTE: Changing magic will at once invalidate all solv file caches.
-    // Helpfull if solv file content must be refreshed (e.g. due to different
-    // repo2* arguments) even if raw metadata are unchanged.
-    // Only values set from a RepoStatus ctor need magic to be added.
+  public:
+    /** Assign data called from RepoStatus ctor (adds magic).
+     *
+     * \Note Changing magic will at once invalidate all solv file caches.
+     * Helpfull if solv file content must be refreshed (e.g. due to different
+     * repo2solv arguments) even if raw metadata are unchanged. Only values
+     * set from a RepoStatus ctor need magic to be added!
+     */
     void assignFromCtor( std::string && checksum_r, Date && timestamp_r )
     {
-      _checksum = std::move(checksum_r);
-      _timestamp = std::move(timestamp_r);
-      if ( !_checksum.empty() )
-      {
+      if ( !checksum_r.empty() ) {
        static const std::string magic( "43" );
-       _checksum += magic;
+       checksum_r += magic;
+       _checksums.insert( std::move(checksum_r) );
       }
+      _timestamp = std::move(timestamp_r);
     }
 
-    /** Recursive computation of max dir timestamp. */
-    static void recursive_timestamp( const Pathname & dir_r, time_t & max_r )
+    /** Inject raw data (no magic added). */
+    void inject( std::string && checksum_r, Date && timestamp_r )
     {
-      std::list<std::string> dircontent;
-      if ( filesystem::readdir( dircontent, dir_r, false/*no dots*/ ) != 0 )
-       return; // readdir logged the error
+      if ( !checksum_r.empty() ) {
+       _checksums.insert( std::move(checksum_r) );
+       _cachedchecksum.reset();
+      }
 
-      for_( it, dircontent.begin(), dircontent.end() )
-      {
-       PathInfo pi( dir_r + *it, PathInfo::LSTAT );
-       if ( pi.isDir() )
-       {
-         if ( pi.mtime() > max_r )
-           max_r = pi.mtime();
-         recursive_timestamp( pi.path(), max_r );
+      if ( timestamp_r > _timestamp )
+       _timestamp = timestamp_r;
+    }
+
+    /** Inject the raw data from rhs */
+    void injectFrom( const Impl & rhs )
+    {
+      if ( &rhs == this )      // no self insert
+       return;
+
+      if ( !rhs._checksums.empty() ) {
+       _checksums.insert( rhs._checksums.begin(), rhs._checksums.end() );
+       _cachedchecksum.reset();
+      }
+
+      if ( rhs._timestamp > _timestamp )
+       _timestamp = rhs._timestamp;
+    }
+
+    bool empty() const
+    { return _checksums.empty(); }
+
+    std::string checksum() const
+    {
+      std::string ret;
+      if ( _checksums.empty() )
+       return ret;
+
+      if ( _checksums.size() == 1 )
+       ret = *_checksums.begin();
+      else {
+       if ( !_cachedchecksum ) {
+         std::stringstream ss;
+         for ( std::string_view c : _checksums )
+           ss << c;
+         _cachedchecksum = CheckSum::sha1(ss).checksum();
        }
+       ret = *_cachedchecksum;
       }
+      return ret;
     }
 
+    Date timestamp() const
+    { return _timestamp; }
+
+    /** Dump to log file (not to/from CookieFile). */
+    std::ostream & dumpOn( std::ostream & str ) const
+    { return str << ( empty() ? "NO_REPOSTATUS" : checksum() ) << " " << time_t(_timestamp); }
+
+  private:
+    Checksums _checksums;
+    Date _timestamp;
+
+    mutable std::optional<std::string> _cachedchecksum;
+
   private:
     friend Impl * rwcowClone<Impl>( const Impl * rhs );
     /** clone for RWCOW_pointer */
@@ -76,10 +146,6 @@ namespace zypp
   };
   ///////////////////////////////////////////////////////////////////
 
-  /** \relates RepoStatus::Impl Stream output */
-  inline std::ostream & operator<<( std::ostream & str, const RepoStatus::Impl & obj )
-  { return str << obj._checksum << " " << (time_t)obj._timestamp; }
-
   ///////////////////////////////////////////////////////////////////
   //
   //   CLASS NAME : RepoStatus
@@ -103,12 +169,18 @@ namespace zypp
       else if ( info.isDir() )
       {
        time_t t = info.mtime();
-       Impl::recursive_timestamp( path_r, t );
+       recursiveTimestamp( path_r, t );
        _pimpl->assignFromCtor( CheckSum::sha1FromString( str::numstring( t ) ).checksum(), Date( t ) );
       }
     }
   }
 
+  RepoStatus::RepoStatus( const RepoInfo & info_r )
+  : _pimpl( new Impl() )
+  {
+    _pimpl->assignFromCtor( CheckSum::sha1FromString( info_r.url().asString() ).checksum(), Date() );
+  }
+
   RepoStatus::RepoStatus( std::string checksum_r, Date timestamp_r )
   : _pimpl( new Impl() )
   {
@@ -128,10 +200,10 @@ namespace zypp
     }
     else
     {
-      // line := "[checksum] time_t"
-      std::string line( str::getline( file ) );
-      ret._pimpl->_timestamp = Date( str::strtonum<time_t>( str::stripLastWord( line ) ) );
-      ret._pimpl->_checksum = line;
+      // line := "[checksum] time_t"  !!! strip time from line
+      std::string line { str::getline( file ) };
+      Date        stmp { str::strtonum<time_t>( str::stripLastWord( line ) ) };
+      ret._pimpl->inject( std::move(line), std::move(stmp) );  // raw inject to avoid magic being added
     }
     return ret;
   }
@@ -142,42 +214,28 @@ namespace zypp
     if (!file) {
       ZYPP_THROW (Exception( "Can't open " + path_r.asString() ) );
     }
-    file << _pimpl->_checksum << " " << (time_t)_pimpl->_timestamp << endl;
+    file << _pimpl->checksum() << " " << time_t(_pimpl->timestamp()) << endl;
     file.close();
   }
 
   bool RepoStatus::empty() const
-  { return _pimpl->_checksum.empty(); }
+  { return _pimpl->empty(); }
 
   Date RepoStatus::timestamp() const
-  { return _pimpl->_timestamp; }
+  { return _pimpl->timestamp(); }
 
   std::ostream & operator<<( std::ostream & str, const RepoStatus & obj )
-  { return str << *obj._pimpl; }
+  { return obj._pimpl->dumpOn( str ); }
 
   RepoStatus operator&&( const RepoStatus & lhs, const RepoStatus & rhs )
   {
-    RepoStatus result;
-
-    if ( lhs.empty() )
-      result = rhs;
-    else if ( rhs.empty() )
-      result = lhs;
-    else
-    {
-      // order strings to assert && is kommutativ
-      std::string lchk( lhs._pimpl->_checksum );
-      std::string rchk( rhs._pimpl->_checksum );
-      std::stringstream ss( lchk < rchk ? lchk+rchk : rchk+lchk );
-
-      result._pimpl->_checksum = CheckSum::sha1(ss).checksum();
-      result._pimpl->_timestamp = std::max( lhs._pimpl->_timestamp, rhs._pimpl->_timestamp );
-    }
+    RepoStatus result { lhs };
+    result._pimpl->injectFrom( *rhs._pimpl );
     return result;
   }
 
   bool operator==( const RepoStatus & lhs, const RepoStatus & rhs )
-  { return lhs._pimpl->_checksum == rhs._pimpl->_checksum; }
+  { return lhs._pimpl->checksum() == rhs._pimpl->checksum(); }
 
   /////////////////////////////////////////////////////////////////
 } // namespace zypp