libzypp 17.31.23
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
19using std::endl;
20
22namespace 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:
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:
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
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
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:531
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:212