1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/base/DrunkenBishop.cc
12 //#include "zypp/base/LogTools.h"
13 #include "zypp/base/Flags.h"
14 #include "zypp/base/String.h"
15 #include "zypp/base/NonCopyable.h"
16 #include "zypp/base/DrunkenBishop.h"
20 ///////////////////////////////////////////////////////////////////
23 ///////////////////////////////////////////////////////////////////
26 ///////////////////////////////////////////////////////////////////
29 /** Direction the drunken Bishop wants to move. */
30 enum class Direction : std::uint8_t // actually 2 bits
38 /** Convert a hex digit (case insensitive) into it's (4bit) integral value.
39 * \throws std::invalid_argument if char
41 inline std::uint8_t hexDigit( char ch_r )
45 case 'F': case 'f': return 15;
46 case 'E': case 'e': return 14;
47 case 'D': case 'd': return 13;
48 case 'C': case 'c': return 12;
49 case 'B': case 'b': return 11;
50 case 'A': case 'a': return 10;
62 throw std::invalid_argument( str::Str() << "Not a hex digit '" << ch_r << "'" );
65 ///////////////////////////////////////////////////////////////////
67 ///////////////////////////////////////////////////////////////////
68 /// \class DrunkenBishop::Impl
69 /// \brief DrunkenBishop implementation.
70 ///////////////////////////////////////////////////////////////////
71 class DrunkenBishop::Impl : private base::NonCopyable
74 /** Default is an empty board. */
83 /** Build up a new board.
84 * \throws std::invalid_argument
86 void compute( const std::string & data_r, const std::string & title_r, unsigned height_r = Auto, unsigned width_r = Auto )
88 // store rendering details
89 _renderSSH = ( data_r.size() <= 32 ); // up to the ssh fingerprint size
90 _fp = str::toUpper( data_r.size() <= 8 ? data_r : data_r.substr( data_r.size()-8 ) );
102 { _w = 19; _h = 11; }
104 else if ( _w == Auto )
109 _board = std::vector<std::uint8_t>( _w*_h, 0 );
110 _s = _w*_h/2; // start
111 _e = _s; // current/end
115 for ( const char * ch = data_r.c_str(); *ch; /*NOOP*/ )
117 std::uint8_t next4 = bite( ch );
121 static const std::uint8_t stepMask(0x3);
122 move( Direction( next4 & stepMask ) );
123 move( Direction( (next4>>2) & stepMask ) );
124 move( Direction( (next4>>4) & stepMask ) );
125 move( Direction( (next4>>6) ) );
129 /** Render board to a stream. */
130 std::ostream & dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
132 if ( _board.empty() )
136 return str << prefix_r << "++" << endl << prefix_r << "++";
139 static const char * colorReset = "\033[0m";
140 static const char * colorBg = "\033[48;5;242m";
141 bool useColor = options_r.testFlag( USE_COLOR );
143 renderTitleOn( str << prefix_r , _tt );
145 for ( unsigned p = 0; p < _board.size(); ++p )
147 if ( ( p % _w ) == 0 )
150 str << ( useColor ? colorReset: "" ) << '|';
151 str << endl << prefix_r << '|' << ( useColor ? colorBg : "" );
153 renderOn( str, useColor, p );
155 str << ( useColor ? colorReset: "" ) << '|';
157 renderTitleOn( str << endl << prefix_r, _fp );
162 /** Increment even width/height values. */
163 static unsigned odd( unsigned val_r )
164 { return( val_r == Auto ? val_r : val_r|1U ); }
166 /** Get next 4 moves (8 bit) from next 2 hex digits (1st digit != '\0' asserted, 0-pad if necessary).
167 * \throws std::invalid_argument if char is not a hex digit or 1st char is \c \0
169 static std::uint8_t bite( const char *& ch_r )
171 std::uint8_t ret = hexDigit( *ch_r ) << 4;
173 ret |= hexDigit( *(ch_r++) );
178 /** Move Bishop from \ref _e into \a direction_r and update the \ref _board. */
179 void move( Direction direction_r )
181 switch ( direction_r )
228 throw std::invalid_argument( str::Str() << "Bad Direction " << unsigned(direction_r) );
234 /** Whether \ref _e is in the top left corner. */
236 { return( _e == 0 ); }
238 /** Whether \ref _e is in the top right corner. */
240 { return( _e == _w-1 ); }
242 /** Whether \ref _e is in the bottom left corner. */
244 { return( _e == _board.size()-_w ); }
246 /** Whether \ref _e is in the bottom right corner. */
248 { return( _e == _board.size()-1 ); }
250 /** Whether \ref _e is in the top row. */
252 { return( _e < _w ); }
254 /** Whether \ref _e is in the bottom row. */
256 { return( _e >= _board.size()-_w ); }
258 /** Whether \ref _e is in the left column. */
260 { return( ( _e % _w ) == 0 ); }
262 /** Whether \ref _e is in the right column. */
264 { return( ( _e % _w ) == (_w-1) ); }
267 /** ANSI color heatmap. */
268 const char * color( std::uint8_t idx_r ) const
270 static const std::vector<const char *> colors = {
272 "\033[38;5;21m", // blue (cold)
276 "\033[38;5;46m", // green
279 "\033[38;5;226m", // yellow
281 "\033[38;5;214m", // orange
284 "\033[38;5;196m", // red
287 "\033[38;5;217m", // pink
289 "\033[38;5;231m", // white (hot)
292 // cycle through heat map to test all colors
295 static unsigned i = 0;
296 if ( ++i == colors.size() )
300 return ( idx_r < colors.size() ? colors[idx_r] : *colors.rbegin() );
303 /** Render non empty title strings */
304 std::ostream & renderTitleOn( std::ostream & str, const std::string & title_r ) const
306 std::string buffer( _w+2, '-' );
307 *buffer.begin() = *buffer.rbegin() = '+';
309 if ( !title_r.empty() && _w >= 2 ) // extra 2 for "[]"
311 std::string::size_type tlen = std::min( title_r.size(), std::string::size_type(_w-2) );
312 std::string::size_type tpos = (_w-tlen)/2; // not (_w-2-tlen) because buffer is size _w+2
313 buffer[tpos++] = '[';
314 for ( std::string::size_type p = 0; p < tlen; ++p, ++tpos )
315 buffer[tpos] = title_r[p];
318 return str << buffer;
321 /** Render board numbers to printable chars. */
322 std::ostream & renderOn( std::ostream & str, bool useColor_r, unsigned pos_r ) const
324 static const std::string sshSet( " .o+=*BOX@%&#/^" );
325 static const std::string gpgSet( " .^:li?(fxXZ#MW&8%@" );
326 const std::string & charSet( _renderSSH ? sshSet : gpgSet );
329 str << color( _board[pos_r] );
337 return str << ( _board[pos_r] < charSet.size() ? charSet[_board[pos_r]] : *charSet.rbegin() );
341 /** Request default width/height values. */
342 static constexpr const unsigned Auto = unsigned(-1);
345 std::vector<std::uint8_t> _board; ///< the board
346 unsigned _h; ///< board height
347 unsigned _w; ///< board with
348 unsigned _s; ///< start position
349 unsigned _e; ///< end position
352 bool _renderSSH; ///< whether to render the ssh (or gpg) char set
353 std::string _fp; ///< fingerprint to render as bottom title
354 std::string _tt; ///< text to render as top title
357 /** Offer default Impl. */
358 static shared_ptr<Impl> nullimpl()
360 static shared_ptr<Impl> _nullimpl( new Impl );
365 ///////////////////////////////////////////////////////////////////
366 // CLASS NAME : DrunkenBishop
367 ///////////////////////////////////////////////////////////////////
369 DrunkenBishop::DrunkenBishop()
370 : _pimpl( Impl::nullimpl() )
371 { /*nothing to compute*/ }
373 DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r )
375 { _pimpl->compute( data_r, title_r ); }
377 DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r )
379 { _pimpl->compute( data_r, title_r, height_r ); }
381 DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r, unsigned width_r )
383 { _pimpl->compute( data_r, title_r, height_r, width_r ); }
385 DrunkenBishop::~DrunkenBishop()
388 std::ostream & DrunkenBishop::dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
389 { return _pimpl->dumpOn( str, prefix_r, options_r ); }
391 std::string DrunkenBishop::asString( const std::string & prefix_r, Options options_r ) const
393 std::ostringstream str;
394 dumpOn( str, prefix_r, options_r );
398 std::vector<std::string> DrunkenBishop::asLines( const std::string & prefix_r, Options options_r ) const
400 std::vector<std::string> ret;
401 str::split( asString( prefix_r, options_r ), std::back_inserter(ret), "\n" );
406 ///////////////////////////////////////////////////////////////////
408 ///////////////////////////////////////////////////////////////////