Codebase list gddrescue / 80e88be0-03d1-4474-b4c2-8ee96293b910/main block.h
80e88be0-03d1-4474-b4c2-8ee96293b910/main

Tree @80e88be0-03d1-4474-b4c2-8ee96293b910/main (Download .tar.gz)

block.h @80e88be0-03d1-4474-b4c2-8ee96293b910/mainraw · history · blame

/* GNU ddrescue - Data recovery tool
   Copyright (C) 2004-2022 Antonio Diaz Diaz.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef LLONG_MAX
#define LLONG_MAX  0x7FFFFFFFFFFFFFFFLL
#endif

// requires '#include <cstdio>' for 'FILE *'

class Block
  {
  long long pos_, size_;  // pos >= 0 && size >= 0 && pos + size <= LLONG_MAX

  void fix_size()		// limit size_ to largest possible value
    { if( size_ < 0 || size_ > LLONG_MAX - pos_ ) size_ = LLONG_MAX - pos_; }

public:
  Block() {}					// default constructor
  Block( const long long p, const long long s ) : pos_( p ), size_( s )
    { if( p < 0 ) { pos_ = 0; if( s > 0 ) size_ -= std::min( s, -p ); }
      fix_size(); }

  long long pos() const { return pos_; }
  long long size() const { return size_; }
  long long end() const { return pos_ + size_; }
  bool full() const { return ( end() >= LLONG_MAX ); }

  void pos( const long long p )
    { pos_ = std::max( p, 0LL );
      if( size_ > LLONG_MAX - pos_ ) size_ = LLONG_MAX - pos_; }
  void shift( const long long offset )
    { if( offset >= 0 )
        { pos_ += std::min( offset, LLONG_MAX - pos_ );
          if( size_ > LLONG_MAX - pos_ ) size_ = LLONG_MAX - pos_; }
      else if( ( pos_ += offset ) < 0 )
        { size_ = std::max( size_ + pos_, 0LL ); pos_ = 0; } }
  void size( const long long s ) { size_ = s; fix_size(); }
  void enlarge( long long s )
    { if( s < 0 ) s = LLONG_MAX;
      if( s > LLONG_MAX - end() ) s = LLONG_MAX - end();
      size_ += s; }
  void end( long long e )			// also moves pos
    { if( e < 0 ) e = LLONG_MAX;
      if( size_ <= e ) pos_ = e - size_; else { pos_ = 0; size_ = e; } }
  Block & assign( const long long p, const long long s )
    {
    pos_ = p; size_ = s;
    if( p < 0 ) { pos_ = 0; if( s > 0 ) size_ -= std::min( s, -p ); }
    fix_size(); return *this;
    }

  void align_pos( const int alignment );
  void align_end( const int alignment );

  bool operator==( const Block & b ) const
    { return pos_ == b.pos_ && size_ == b.size_; }
  bool operator!=( const Block & b ) const
    { return pos_ != b.pos_ || size_ != b.size_; }

  bool operator<( const Block & b ) const { return ( end() <= b.pos() ); }

  bool follows( const Block & b ) const
    { return ( pos_ == b.end() ); }
  bool includes( const Block & b ) const
    { return ( pos_ <= b.pos_ && end() >= b.end() ); }
  bool includes( const long long pos ) const
    { return ( pos_ <= pos && end() > pos ); }
  bool strictly_includes( const long long pos ) const
    { return ( pos_ < pos && end() > pos ); }
  bool overlaps( const Block & b ) const
    { return ( pos_ < b.end() && b.pos_ < end() ); }

  void crop( const Block & b );
  bool join( const Block & b );			// join contiguous blocks
  void shift_boundary( Block & b, const long long pos );
  Block split( long long pos, const int hardbs = 1 );
  };


class Sblock : public Block
  {
public:
  enum Status		// ordered from less to more processed state
    { non_tried = '?', non_trimmed = '*', non_scraped = '/',
      bad_sector = '-', finished = '+' };
private:
  Status status_;

public:
  Sblock() {}					// default constructor
  Sblock( const Block & b, const Status st )
    : Block( b ), status_( st ) {}
  Sblock( const long long p, const long long s, const Status st )
    : Block( p, s ), status_( st ) {}

  Status status() const { return status_; }
  void status( const Status st ) { status_ = st; }

  bool operator!=( const Sblock & sb ) const
    { return Block::operator!=( sb ) || status_ != sb.status_; }

  bool join( const Sblock & sb )
    { if( status_ == sb.status_ ) return Block::join( sb ); else return false; }
  Sblock split( const long long pos, const int hardbs = 1 )
    { return Sblock( Block::split( pos, hardbs ), status_ ); }
  static bool isstatus( const int st )
    { return ( st == non_tried || st == non_trimmed || st == non_scraped ||
               st == bad_sector || st == finished ); }
  static bool is_good_status( const Status st ) { return st != bad_sector; }
  static int processed_state( const Status st )
    {
    switch( st )
      {
      case non_tried:   return 0;
      case non_trimmed: return 1;
      case non_scraped: return 2;
      case bad_sector:  return 3;
      default:          return 4;
      }
    }
  };


class Domain
  {
  std::vector< Block > block_vector;	// blocks are ordered and don't overlap
  mutable long long cached_in_size;
  void reset_cached_in_size() { cached_in_size = -1; }

public:
  Domain( const long long p, const long long s,
          const char * const mapname = 0, const bool loose = false );

  long long pos() const { return block_vector.front().pos(); }
  long long end() const { return block_vector.back().end(); }
  long long size() const { return end() - pos(); }
  const Block & block( const long i ) const { return block_vector[i]; }
  long blocks() const { return block_vector.size(); }
  bool empty() const { return ( end() <= pos() ); }
  bool full() const { return ( !empty() && end() >= LLONG_MAX ); }

  long long in_size() const
    {
    if( cached_in_size < 0 )
      {
      cached_in_size = 0;
      for( unsigned long i = 0; i < block_vector.size(); ++i )
        cached_in_size += block_vector[i].size();
      }
    return cached_in_size;
    }

  bool operator!=( const Domain & d ) const
    {
    if( block_vector.size() != d.block_vector.size() ) return true;
    for( unsigned long i = 0; i < block_vector.size(); ++i )
      if( block_vector[i] != d.block_vector[i] ) return true;
    return false;
    }

  bool operator<( const Block & b ) const { return ( end() <= b.pos() ); }
  bool operator>( const Block & b ) const { return ( pos() >= b.end() ); }

  bool includes( const Block & b ) const
    {
    unsigned long l = 0, r = block_vector.size();
    while( l < r )
      {
      const long m = ( l + r ) / 2;
      const Block & db = block_vector[m];
      if( db.includes( b ) ) return true;
      if( db < b ) l = m + 1; else if( b < db ) r = m; else break;
      }
    return false;
    }

  bool includes( const long long pos ) const
    {
    for( unsigned long i = 0; i < block_vector.size(); ++i )
      if( block_vector[i].includes( pos ) ) return true;
    return false;
    }

  bool overlaps( const Block & b ) const
    {
    unsigned long l = 0, r = block_vector.size();
    while( l < r )
      {
      const long m = ( l + r ) / 2;
      const Block & db = block_vector[m];
      if( db.overlaps( b ) ) return true;
      if( db < b ) l = m + 1; else if( b < db ) r = m; else break;
      }
    return false;
    }

  void clear()
    {
    block_vector.clear(); block_vector.push_back( Block( 0, 0 ) );
    cached_in_size = 0;
    }

  void crop( const Block & b );
  void crop_by_file_size( const long long size ) { crop( Block( 0, size ) ); }
  };


class Mapfile
  {
public:
  enum Status
    { copying = '?', trimming = '*', scraping = '/', retrying = '-',
      filling = 'F', generating = 'G', finished = '+' };

private:
  long long current_pos_;
  const char * const filename_;
  std::string current_msg;
  Status current_status_;
  int current_pass_;
  mutable long index_;			// cached index of last find or change
  bool read_only_;
  std::vector< Sblock > sblock_vector;	// note: blocks are consecutive

  void insert_sblock( const long i, const Sblock & sb )	// insert before i
    { sblock_vector.insert( sblock_vector.begin() + i, sb ); }

public:
  explicit Mapfile( const char * const mapname )
    : current_pos_( 0 ), filename_( mapname ), current_status_( copying ),
      current_pass_( 1 ), index_( 0 ), read_only_( false ) {}

  void compact_sblock_vector();
  void join_subsectors( const int hardbs );
  void extend_sblock_vector( const long long insize );
  void shift_blocks( const long long offset, const Sblock::Status st );
  bool truncate_vector( const long long end, const bool force = false );
  void set_to_status( const Sblock::Status st )
    { sblock_vector.assign( 1, Sblock( 0, -1, st ) ); }
  bool read_mapfile( const int default_sblock_status = 0, const bool ro = true );
  bool write_mapfile( FILE * f = 0, const bool timestamp = false,
                      const bool mf_sync = false,
                      const Domain * const annotate_domainp = 0 ) const;

  bool blank() const;			// empty or all blocks non_tried
  long long current_pos() const { return current_pos_; }
  Status current_status() const { return current_status_; }
  int current_pass() const { return current_pass_; }
  const char * filename() const { return filename_; }
  const char * pname( const bool in = true ) const;	// printable name
  bool read_only() const { return read_only_; }

  void current_pos( const long long pos ) { current_pos_ = pos; }
  void current_status( const Status st, const char * const msg = "" )
    { current_status_ = st;
      current_msg = ( st == finished ) ? "Finished" : msg; }
  void current_pass( const int pass ) { current_pass_ = pass; }

  Block extent() const			// pos of first block may be > 0
    { if( sblock_vector.empty() ) return Block( 0, 0 );
      return Block( sblock_vector.front().pos(),
                    sblock_vector.back().end() - sblock_vector.front().pos() ); }
  const Sblock & sblock( const long i ) const { return sblock_vector[i]; }
  long sblocks() const { return sblock_vector.size(); }
  void change_sblock_status( const long i, const Sblock::Status st )
    { sblock_vector[i].status( st ); }

  void split_by_domain_borders( const Domain & domain );
  void split_by_mapfile_borders( const Mapfile & mapfile );
  bool try_split_sblock_by( const long long pos, const long i )
    {
    if( sblock_vector[i].strictly_includes( pos ) )
      { insert_sblock( i, sblock_vector[i].split( pos ) ); return true; }
    return false;
    }

  long find_index( const long long pos ) const;
  bool find_chunk( Block & b, const Sblock::Status st,
                   const Domain & domain, const int alignment,
                   const bool after_finished = false,
                   const bool unfinished = false ) const;
  bool rfind_chunk( Block & b, const Sblock::Status st,
                    const Domain & domain, const int alignment,
                    const bool before_finished = false ) const;
  int change_chunk_status( const Block & b, const Sblock::Status st,
                           const Domain & domain,
                           Sblock::Status * const old_stp = 0 );

  static bool isstatus( const int st )
    { return ( st == copying || st == trimming || st == scraping ||
               st == retrying || st == filling || st == generating ||
               st == finished ); }
  static const char * status_name( const Status st );
  };


// Defined in main_common.cc
extern int verbosity;
void show_error( const char * const msg, const int errcode = 0,
                 const bool help = false );
void show_file_error( const char * const filename, const char * const msg,
                      const int errcode = 0 );
void internal_error( const char * const msg );
int empty_domain();
int not_readable( const char * const mapname );
int not_writable( const char * const mapname );
long initial_time();
bool write_file_header( FILE * const f, const char * const filetype );
bool write_timestamp( FILE * const f );
bool write_final_timestamp( FILE * const f );
const char * format_num( long long num, long long limit = 999999,
                         const int set_prefix = 0 );
const char * format_num3( long long num );
const char * format_percentage( long long num, long long den,
                                const int iwidth = 3, int prec = -2,
                                const bool rounding = true );