libzypp  17.31.0
DrunkenBishop.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
11 #include <cstdint>
12 #include <iostream>
13 //#include <zypp/base/LogTools.h>
14 #include <zypp/base/Flags.h>
15 #include <zypp/base/String.h>
16 #include <zypp/base/NonCopyable.h>
18 
19 using std::endl;
20 
22 namespace zypp
23 {
25  namespace base
26  {
28  namespace
29  {
31  enum class Direction : std::uint8_t // actually 2 bits
32  {
33  NW = 0x0,
34  NE = 0x1,
35  SW = 0x2,
36  SE = 0x3,
37  };
38 
42  inline std::uint8_t hexDigit( char ch_r )
43  {
44  switch ( ch_r )
45  {
46  case 'F': case 'f': return 15;
47  case 'E': case 'e': return 14;
48  case 'D': case 'd': return 13;
49  case 'C': case 'c': return 12;
50  case 'B': case 'b': return 11;
51  case 'A': case 'a': return 10;
52  case '9': return 9;
53  case '8': return 8;
54  case '7': return 7;
55  case '6': return 6;
56  case '5': return 5;
57  case '4': return 4;
58  case '3': return 3;
59  case '2': return 2;
60  case '1': return 1;
61  case '0': return 0;
62  }
63  throw std::invalid_argument( str::Str() << "Not a hex digit '" << ch_r << "'" );
64  }
65  } // namespace
67 
73  {
74  public:
76  Impl()
77  : _h( 0U )
78  , _w( 0u )
79  , _s( 0U )
80  , _e( 0U )
81  , _renderSSH( true )
82  {}
83 
87  void compute( const std::string & data_r, const std::string & title_r, unsigned height_r = Auto, unsigned width_r = Auto )
88  {
89  // store rendering details
90  _renderSSH = ( data_r.size() <= 32 ); // up to the ssh fingerprint size
91  _fp = str::toUpper( data_r.size() <= 8 ? data_r : data_r.substr( data_r.size()-8 ) );
92  _tt = title_r;
93 
94  // init the board
95  _h = odd(height_r);
96  _w = odd(width_r);
97 
98  if ( _h == Auto )
99  {
100  if ( _renderSSH )
101  { _w = 17; _h = 9; }
102  else
103  { _w = 19; _h = 11; }
104  }
105  else if ( _w == Auto )
106  {
107  _w = (2*_h)-1;
108  }
109 
110  _board = std::vector<std::uint8_t>( _w*_h, 0 );
111  _s = _w*_h/2; // start
112  _e = _s; // current/end
113  ++_board[_e];
114 
115  // go
116  for ( const char * ch = data_r.c_str(); *ch; /*NOOP*/ )
117  {
118  std::uint8_t next4 = bite( ch );
119  // next4: 0x94
120  // bits: 10 01 01 00
121  // step: 4 3 2 1
122  static const std::uint8_t stepMask(0x3);
123  move( Direction( next4 & stepMask ) );
124  move( Direction( (next4>>2) & stepMask ) );
125  move( Direction( (next4>>4) & stepMask ) );
126  move( Direction( (next4>>6) ) );
127  }
128  }
129 
131  std::ostream & dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
132  {
133  if ( _board.empty() )
134  {
135  // "++\n"
136  // "++"
137  return str << prefix_r << "++" << endl << prefix_r << "++";
138  }
139 
140  static const char * colorReset = "\033[0m";
141  static const char * colorBg = "\033[48;5;242m";
142  bool useColor = options_r.testFlag( USE_COLOR );
143 
144  renderTitleOn( str << prefix_r , _tt );
145 
146  for ( unsigned p = 0; p < _board.size(); ++p )
147  {
148  if ( ( p % _w ) == 0 )
149  {
150  if ( p )
151  str << ( useColor ? colorReset: "" ) << '|';
152  str << endl << prefix_r << '|' << ( useColor ? colorBg : "" );
153  }
154  renderOn( str, useColor, p );
155  }
156  str << ( useColor ? colorReset: "" ) << '|';
157 
158  renderTitleOn( str << endl << prefix_r, _fp );
159  return str;
160  }
161 
162  private:
164  static unsigned odd( unsigned val_r )
165  { return( val_r == Auto ? val_r : val_r|1U ); }
166 
170  static std::uint8_t bite( const char *& ch_r )
171  {
172  std::uint8_t ret = hexDigit( *ch_r ) << 4;
173  if ( *(++ch_r) )
174  ret |= hexDigit( *(ch_r++) );
175  return ret;
176  }
177 
178  private:
180  void move( Direction direction_r )
181  {
182  switch ( direction_r )
183  {
184  case Direction::NW:
185  if ( atTL() )
186  /*no move*/;
187  else if ( atT() )
188  _e -= 1;
189  else if ( atL() )
190  _e -= _w;
191  else
192  _e -= _w+1;
193  break;
194 
195  case Direction::NE:
196  if ( atTR() )
197  /*no move*/;
198  else if ( atT() )
199  _e += 1;
200  else if ( atR() )
201  _e -= _w;
202  else
203  _e -= _w-1;
204  break;
205 
206  case Direction::SW:
207  if ( atBL() )
208  /*no move*/;
209  else if ( atB() )
210  _e -= 1;
211  else if ( atL() )
212  _e += _w;
213  else
214  _e += _w-1;
215  break;
216 
217  case Direction::SE:
218  if ( atBR() )
219  /*no move*/;
220  else if ( atB() )
221  _e += 1;
222  else if ( atR() )
223  _e += _w;
224  else
225  _e += _w+1;
226  break;
227 
228  default:
229  throw std::invalid_argument( str::Str() << "Bad Direction " << unsigned(direction_r) );
230  }
231  // update the board
232  ++_board[_e];
233  }
234 
236  bool atTL() const
237  { return( _e == 0 ); }
238 
240  bool atTR() const
241  { return( _e == _w-1 ); }
242 
244  bool atBL() const
245  { return( _e == _board.size()-_w ); }
246 
248  bool atBR() const
249  { return( _e == _board.size()-1 ); }
250 
252  bool atT() const
253  { return( _e < _w ); }
254 
256  bool atB() const
257  { return( _e >= _board.size()-_w ); }
258 
260  bool atL() const
261  { return( ( _e % _w ) == 0 ); }
262 
264  bool atR() const
265  { return( ( _e % _w ) == (_w-1) ); }
266 
267  private:
269  const char * color( std::uint8_t idx_r ) const
270  {
271  static const std::vector<const char *> colors = {
272  "", // no coin
273  "\033[38;5;21m", // blue (cold)
274  "\033[38;5;39m",
275  "\033[38;5;50m",
276  "\033[38;5;48m",
277  "\033[38;5;46m", // green
278  "\033[38;5;118m",
279  "\033[38;5;190m",
280  "\033[38;5;226m", // yellow
281  "\033[38;5;220m",
282  "\033[38;5;214m", // orange
283  "\033[38;5;208m",
284  "\033[38;5;202m",
285  "\033[38;5;196m", // red
286  "\033[38;5;203m",
287  "\033[38;5;210m",
288  "\033[38;5;217m", // pink
289  "\033[38;5;224m",
290  "\033[38;5;231m", // white (hot)
291  };
292 #if 0
293  // cycle through heat map to test all colors
294  if ( ! idx_r )
295  return "";
296  static unsigned i = 0;
297  if ( ++i == colors.size() )
298  i = 1;
299  return colors[i];
300 #endif
301  return ( idx_r < colors.size() ? colors[idx_r] : *colors.rbegin() );
302  }
303 
305  std::ostream & renderTitleOn( std::ostream & str, const std::string & title_r ) const
306  {
307  std::string buffer( _w+2, '-' );
308  *buffer.begin() = *buffer.rbegin() = '+';
309 
310  if ( !title_r.empty() && _w >= 2 ) // extra 2 for "[]"
311  {
312  std::string::size_type tlen = std::min( title_r.size(), std::string::size_type(_w-2) );
313  std::string::size_type tpos = (_w-tlen)/2; // not (_w-2-tlen) because buffer is size _w+2
314  buffer[tpos++] = '[';
315  for ( std::string::size_type p = 0; p < tlen; ++p, ++tpos )
316  buffer[tpos] = title_r[p];
317  buffer[tpos] = ']';
318  }
319  return str << buffer;
320  }
321 
323  std::ostream & renderOn( std::ostream & str, bool useColor_r, unsigned pos_r ) const
324  {
325  static const std::string sshSet( " .o+=*BOX@%&#/^" );
326  static const std::string gpgSet( " .^:li?(fxXZ#MW&8%@" );
327  const std::string & charSet( _renderSSH ? sshSet : gpgSet );
328 
329  if ( useColor_r )
330  str << color( _board[pos_r] );
331 
332  if ( pos_r == _e )
333  return str << 'E';
334 
335  if ( pos_r == _s )
336  return str << 'S';
337 
338  return str << ( _board[pos_r] < charSet.size() ? charSet[_board[pos_r]] : *charSet.rbegin() );
339  }
340 
341  private:
343  static constexpr const unsigned Auto = unsigned(-1);
344 
345  private:
346  std::vector<std::uint8_t> _board;
347  unsigned _h;
348  unsigned _w;
349  unsigned _s;
350  unsigned _e;
351 
352  private:
353  bool _renderSSH;
354  std::string _fp;
355  std::string _tt;
356 
357  public:
359  static shared_ptr<Impl> nullimpl()
360  {
361  static shared_ptr<Impl> _nullimpl( new Impl );
362  return _nullimpl;
363  }
364  };
365 
367  // CLASS NAME : DrunkenBishop
369 
371  : _pimpl( Impl::nullimpl() )
372  { /*nothing to compute*/ }
373 
374  DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r )
375  : _pimpl( new Impl )
376  { _pimpl->compute( data_r, title_r ); }
377 
378  DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r )
379  : _pimpl( new Impl )
380  { _pimpl->compute( data_r, title_r, height_r ); }
381 
382  DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r, unsigned width_r )
383  : _pimpl( new Impl )
384  { _pimpl->compute( data_r, title_r, height_r, width_r ); }
385 
387  {}
388 
389  std::ostream & DrunkenBishop::dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
390  { return _pimpl->dumpOn( str, prefix_r, options_r ); }
391 
392  std::string DrunkenBishop::asString( const std::string & prefix_r, Options options_r ) const
393  {
394  std::ostringstream str;
395  dumpOn( str, prefix_r, options_r );
396  return str.str();
397  }
398 
399  std::vector<std::string> DrunkenBishop::asLines( const std::string & prefix_r, Options options_r ) const
400  {
401  std::vector<std::string> ret;
402  str::split( asString( prefix_r, options_r ), std::back_inserter(ret), "\n" );
403  return ret;
404  }
405 
406  } // namespace base
408 } // namespace zypp
bool atB() const
Whether _e is in the bottom row.
DrunkenBishop()
Default ctor: empty board (1x1)
DrunkenBishop implementation.
std::ostream & renderOn(std::ostream &str, bool useColor_r, unsigned pos_r) const
Render board numbers to printable chars.
bool atTL() const
Whether _e is in the top left corner.
std::ostream & dumpOn(std::ostream &str, const std::string &prefix_r, Options options_r) const
Render board to a stream.
bool atT() const
Whether _e is in the top row.
void move(Direction direction_r)
Move Bishop from _e into direction_r and update the _board.
bool atBR() const
Whether _e is in the bottom right corner.
String related utilities and Regular expression matching.
std::ostream & renderTitleOn(std::ostream &str, const std::string &title_r) const
Render non empty title strings.
Impl()
Default is an empty board.
std::vector< std::uint8_t > _board
the board
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition: NonCopyable.h:26
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \, const Trim trim_r=NO_TRIM)
Split line_r into words.
Definition: String.h:531
bool atTR() const
Whether _e is in the top right corner.
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:211
std::string _fp
fingerprint to render as bottom title
bool atL() const
Whether _e is in the left column.
std::ostream & dumpOn(std::ostream &str, Options options_r=Options()) const
Render board to steam.
Definition: DrunkenBishop.h:97
const char * color(std::uint8_t idx_r) const
ANSI color heatmap.
std::vector< std::string > asLines(Options options_r=Options()) const
Render to an array of lines.
SolvableIdType size_type
Definition: PoolMember.h:126
std::string asString(Options options_r=Options()) const
Render board as string.
static constexpr const unsigned Auto
Request default width/height values.
bool _renderSSH
whether to render the ssh (or gpg) char set
bool atR() const
Whether _e is in the right column.
void compute(const std::string &data_r, const std::string &title_r, unsigned height_r=Auto, unsigned width_r=Auto)
Build up a new board.
static unsigned odd(unsigned val_r)
Increment even width/height values.
std::string _tt
text to render as top title
RW_pointer< Impl > _pimpl
Implementation class.
bool atBL() const
Whether _e is in the bottom left corner.
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
unsigned _s
start position
std::string toUpper(const std::string &s)
Return uppercase version of s.
Definition: String.cc:200
Direction
Direction the drunken Bishop wants to move.
static shared_ptr< Impl > nullimpl()
Offer default Impl.
static std::uint8_t bite(const char *&ch_r)
Get next 4 moves (8 bit) from next 2 hex digits (1st digit != &#39;\0&#39; asserted, 0-pad if necessary)...