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