libzypp 17.38.3
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
14#include "DrunkenBishop.h"
15
16//#include <zypp-core/base/LogTools.h>
20
21
22using std::endl;
23
25namespace zypp
26{
28 namespace base
29 {
31 namespace
32 {
34 enum class Direction : std::uint8_t // actually 2 bits
35 {
36 NW = 0x0,
37 NE = 0x1,
38 SW = 0x2,
39 SE = 0x3,
40 };
41
45 inline std::uint8_t hexDigit( char ch_r )
46 {
47 switch ( ch_r )
48 {
49 case 'F': case 'f': return 15;
50 case 'E': case 'e': return 14;
51 case 'D': case 'd': return 13;
52 case 'C': case 'c': return 12;
53 case 'B': case 'b': return 11;
54 case 'A': case 'a': return 10;
55 case '9': return 9;
56 case '8': return 8;
57 case '7': return 7;
58 case '6': return 6;
59 case '5': return 5;
60 case '4': return 4;
61 case '3': return 3;
62 case '2': return 2;
63 case '1': return 1;
64 case '0': return 0;
65 }
66 throw std::invalid_argument( str::Str() << "Not a hex digit '" << ch_r << "'" );
67 }
68 } // namespace
70
76 {
77 public:
80 : _h( 0U )
81 , _w( 0u )
82 , _s( 0U )
83 , _e( 0U )
84 , _renderSSH( true )
85 {}
86
90 void compute( const std::string & data_r, const std::string & title_r, unsigned height_r = Auto, unsigned width_r = Auto )
91 {
92 // store rendering details
93 _renderSSH = ( data_r.size() <= 32 ); // up to the ssh fingerprint size
94 _fp = str::toUpper( data_r.size() <= 8 ? data_r : data_r.substr( data_r.size()-8 ) );
95 _tt = title_r;
96
97 // init the board
98 _h = odd(height_r);
99 _w = odd(width_r);
100
101 if ( _h == Auto )
102 {
103 if ( _renderSSH )
104 { _w = 17; _h = 9; }
105 else
106 { _w = 19; _h = 11; }
107 }
108 else if ( _w == Auto )
109 {
110 _w = (2*_h)-1;
111 }
112
113 _board = std::vector<std::uint8_t>( _w*_h, 0 );
114 _s = _w*_h/2; // start
115 _e = _s; // current/end
116 ++_board[_e];
117
118 // go
119 for ( const char * ch = data_r.c_str(); *ch; /*NOOP*/ )
120 {
121 std::uint8_t next4 = bite( ch );
122 // next4: 0x94
123 // bits: 10 01 01 00
124 // step: 4 3 2 1
125 static const std::uint8_t stepMask(0x3);
126 move( Direction( next4 & stepMask ) );
127 move( Direction( (next4>>2) & stepMask ) );
128 move( Direction( (next4>>4) & stepMask ) );
129 move( Direction( (next4>>6) ) );
130 }
131 }
132
134 std::ostream & dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
135 {
136 if ( _board.empty() )
137 {
138 // "++\n"
139 // "++"
140 return str << prefix_r << "++" << endl << prefix_r << "++";
141 }
142
143 static const char * colorReset = "\033[0m";
144 static const char * colorBg = "\033[48;5;242m";
145 bool useColor = options_r.testFlag( USE_COLOR );
146
147 renderTitleOn( str << prefix_r , _tt );
148
149 for ( unsigned p = 0; p < _board.size(); ++p )
150 {
151 if ( ( p % _w ) == 0 )
152 {
153 if ( p )
154 str << ( useColor ? colorReset: "" ) << '|';
155 str << endl << prefix_r << '|' << ( useColor ? colorBg : "" );
156 }
157 renderOn( str, useColor, p );
158 }
159 str << ( useColor ? colorReset: "" ) << '|';
160
161 renderTitleOn( str << endl << prefix_r, _fp );
162 return str;
163 }
164
165 private:
167 static unsigned odd( unsigned val_r )
168 { return( val_r == Auto ? val_r : val_r|1U ); }
169
173 static std::uint8_t bite( const char *& ch_r )
174 {
175 std::uint8_t ret = hexDigit( *ch_r ) << 4;
176 if ( *(++ch_r) )
177 ret |= hexDigit( *(ch_r++) );
178 return ret;
179 }
180
181 private:
183 void move( Direction direction_r )
184 {
185 switch ( direction_r )
186 {
187 case Direction::NW:
188 if ( atTL() )
189 /*no move*/;
190 else if ( atT() )
191 _e -= 1;
192 else if ( atL() )
193 _e -= _w;
194 else
195 _e -= _w+1;
196 break;
197
198 case Direction::NE:
199 if ( atTR() )
200 /*no move*/;
201 else if ( atT() )
202 _e += 1;
203 else if ( atR() )
204 _e -= _w;
205 else
206 _e -= _w-1;
207 break;
208
209 case Direction::SW:
210 if ( atBL() )
211 /*no move*/;
212 else if ( atB() )
213 _e -= 1;
214 else if ( atL() )
215 _e += _w;
216 else
217 _e += _w-1;
218 break;
219
220 case Direction::SE:
221 if ( atBR() )
222 /*no move*/;
223 else if ( atB() )
224 _e += 1;
225 else if ( atR() )
226 _e += _w;
227 else
228 _e += _w+1;
229 break;
230
231 default:
232 throw std::invalid_argument( str::Str() << "Bad Direction " << unsigned(direction_r) );
233 }
234 // update the board
235 ++_board[_e];
236 }
237
239 bool atTL() const
240 { return( _e == 0 ); }
241
243 bool atTR() const
244 { return( _e == _w-1 ); }
245
247 bool atBL() const
248 { return( _e == _board.size()-_w ); }
249
251 bool atBR() const
252 { return( _e == _board.size()-1 ); }
253
255 bool atT() const
256 { return( _e < _w ); }
257
259 bool atB() const
260 { return( _e >= _board.size()-_w ); }
261
263 bool atL() const
264 { return( ( _e % _w ) == 0 ); }
265
267 bool atR() const
268 { return( ( _e % _w ) == (_w-1) ); }
269
270 private:
272 const char * color( std::uint8_t idx_r ) const
273 {
274 static const std::vector<const char *> colors = {
275 "", // no coin
276 "\033[38;5;21m", // blue (cold)
277 "\033[38;5;39m",
278 "\033[38;5;50m",
279 "\033[38;5;48m",
280 "\033[38;5;46m", // green
281 "\033[38;5;118m",
282 "\033[38;5;190m",
283 "\033[38;5;226m", // yellow
284 "\033[38;5;220m",
285 "\033[38;5;214m", // orange
286 "\033[38;5;208m",
287 "\033[38;5;202m",
288 "\033[38;5;196m", // red
289 "\033[38;5;203m",
290 "\033[38;5;210m",
291 "\033[38;5;217m", // pink
292 "\033[38;5;224m",
293 "\033[38;5;231m", // white (hot)
294 };
295#if 0
296 // cycle through heat map to test all colors
297 if ( ! idx_r )
298 return "";
299 static unsigned i = 0;
300 if ( ++i == colors.size() )
301 i = 1;
302 return colors[i];
303#endif
304 return ( idx_r < colors.size() ? colors[idx_r] : *colors.rbegin() );
305 }
306
308 std::ostream & renderTitleOn( std::ostream & str, const std::string & title_r ) const
309 {
310 std::string buffer( _w+2, '-' );
311 *buffer.begin() = *buffer.rbegin() = '+';
312
313 if ( !title_r.empty() && _w >= 2 ) // extra 2 for "[]"
314 {
315 std::string::size_type tlen = std::min( title_r.size(), std::string::size_type(_w-2) );
316 std::string::size_type tpos = (_w-tlen)/2; // not (_w-2-tlen) because buffer is size _w+2
317 buffer[tpos++] = '[';
318 for ( std::string::size_type p = 0; p < tlen; ++p, ++tpos )
319 buffer[tpos] = title_r[p];
320 buffer[tpos] = ']';
321 }
322 return str << buffer;
323 }
324
326 std::ostream & renderOn( std::ostream & str, bool useColor_r, unsigned pos_r ) const
327 {
328 static const std::string sshSet( " .o+=*BOX@%&#/^" );
329 static const std::string gpgSet( " .^:li?(fxXZ#MW&8%@" );
330 const std::string & charSet( _renderSSH ? sshSet : gpgSet );
331
332 if ( useColor_r )
333 str << color( _board[pos_r] );
334
335 if ( pos_r == _e )
336 return str << 'E';
337
338 if ( pos_r == _s )
339 return str << 'S';
340
341 return str << ( _board[pos_r] < charSet.size() ? charSet[_board[pos_r]] : *charSet.rbegin() );
342 }
343
344 private:
346 static constexpr const unsigned Auto = unsigned(-1);
347
348 private:
349 std::vector<std::uint8_t> _board;
350 unsigned _h;
351 unsigned _w;
352 unsigned _s;
353 unsigned _e;
354
355 private:
357 std::string _fp;
358 std::string _tt;
359
360 public:
363 {
364 static shared_ptr<Impl> _nullimpl( new Impl );
365 return _nullimpl;
366 }
367 };
368
370 // CLASS NAME : DrunkenBishop
372
374 : _pimpl( Impl::nullimpl() )
375 { /*nothing to compute*/ }
376
377 DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r )
378 : _pimpl( new Impl )
379 { _pimpl->compute( data_r, title_r ); }
380
381 DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r )
382 : _pimpl( new Impl )
383 { _pimpl->compute( data_r, title_r, height_r ); }
384
385 DrunkenBishop::DrunkenBishop( const std::string & data_r, const std::string & title_r, unsigned height_r, unsigned width_r )
386 : _pimpl( new Impl )
387 { _pimpl->compute( data_r, title_r, height_r, width_r ); }
388
391
392 std::ostream & DrunkenBishop::dumpOn( std::ostream & str, const std::string & prefix_r, Options options_r ) const
393 { return _pimpl->dumpOn( str, prefix_r, options_r ); }
394
395 std::string DrunkenBishop::asString( const std::string & prefix_r, Options options_r ) const
396 {
397 std::ostringstream str;
398 dumpOn( str, prefix_r, options_r );
399 return str.str();
400 }
401
402 std::vector<std::string> DrunkenBishop::asLines( const std::string & prefix_r, Options options_r ) const
403 {
404 std::vector<std::string> ret;
405 str::split( asString( prefix_r, options_r ), std::back_inserter(ret), "\n" );
406 return ret;
407 }
408
409 } // namespace base
411} // namespace zypp
DrunkenBishop implementation.
bool atL() const
Whether _e is in the left column.
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.
String related utilities and Regular expression matching.
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition NonCopyable.h:26
std::string toUpper(const std::string &s)
Return uppercase version of s.
Definition String.cc:203
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:602
Easy-to use interface to the ZYPP dependency resolver.
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:213