@@ -205,121 +205,155 @@ struct rgb {
205
205
206
206
namespace detail {
207
207
208
- // color is a struct of either a rgb color or a terminal color.
208
+ // a bit-packed variant of an RGB color, a terminal color, or unset color.
209
+ // see text_style for the bit-packing scheme.
209
210
struct color_type {
210
- FMT_CONSTEXPR color_type () noexcept : is_rgb(), value{} {}
211
- FMT_CONSTEXPR color_type (color rgb_color) noexcept : is_rgb(true ), value{} {
212
- value.rgb_color = static_cast <uint32_t >(rgb_color);
213
- }
214
- FMT_CONSTEXPR color_type (rgb rgb_color) noexcept : is_rgb(true ), value{} {
215
- value.rgb_color = (static_cast <uint32_t >(rgb_color.r ) << 16 ) |
216
- (static_cast <uint32_t >(rgb_color.g ) << 8 ) | rgb_color.b ;
217
- }
211
+ FMT_CONSTEXPR color_type () noexcept = default;
212
+ FMT_CONSTEXPR color_type (color rgb_color) noexcept
213
+ : value_(static_cast <uint32_t >(rgb_color) | (1 << 24 )) {}
214
+ FMT_CONSTEXPR color_type (rgb rgb_color) noexcept
215
+ : color_type(static_cast <color>(
216
+ (static_cast <uint32_t >(rgb_color.r) << 16) |
217
+ (static_cast <uint32_t >(rgb_color.g) << 8) | rgb_color.b)) {}
218
218
FMT_CONSTEXPR color_type (terminal_color term_color) noexcept
219
- : is_rgb(), value{} {
220
- value.term_color = static_cast <uint8_t >(term_color);
219
+ : value_(static_cast <uint32_t >(term_color) | (3 << 24 )) {}
220
+
221
+ FMT_CONSTEXPR auto is_terminal_color () const noexcept -> bool {
222
+ return (value_ & (1 << 25 )) != 0 ;
223
+ }
224
+
225
+ FMT_CONSTEXPR auto value () const noexcept -> uint32_t {
226
+ return value_ & 0xFFFFFF ;
221
227
}
222
- bool is_rgb;
223
- union color_union {
224
- uint8_t term_color;
225
- uint32_t rgb_color;
226
- } value;
228
+
229
+ FMT_CONSTEXPR color_type (uint32_t value) noexcept : value_(value) {}
230
+
231
+ uint32_t value_{};
227
232
};
228
233
} // namespace detail
229
234
230
235
// / A text style consisting of foreground and background colors and emphasis.
231
236
class text_style {
237
+ // The information is packed as follows:
238
+ // ┌──┐
239
+ // │ 0│─┐
240
+ // │..│ ├── foreground color value
241
+ // │23│─┘
242
+ // ├──┤
243
+ // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
244
+ // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
245
+ // ├──┤
246
+ // │26│──── overflow bit, always zero (see below)
247
+ // ├──┤
248
+ // │27│─┐
249
+ // │..│ │
250
+ // │50│ │
251
+ // ├──┤ │
252
+ // │51│ ├── background color (same format as the foreground color)
253
+ // │52│ │
254
+ // ├──┤ │
255
+ // │53│─┘
256
+ // ├──┤
257
+ // │54│─┐
258
+ // │..│ ├── emphases
259
+ // │61│─┘
260
+ // ├──┤
261
+ // │62│─┬── unused
262
+ // │63│─┘
263
+ // └──┘
264
+ // The overflow bits are there to make operator|= efficient.
265
+ // When ORing, we must throw if, for either the foreground or background,
266
+ // one style specifies a terminal color and the other specifies any color
267
+ // (terminal or RGB); in other words, if one discriminator is 11 and the
268
+ // other is 11 or 01.
269
+ //
270
+ // We do that check by adding the styles. Consider what adding does to each
271
+ // possible pair of discriminators:
272
+ // 00 + 00 = 000
273
+ // 01 + 00 = 001
274
+ // 11 + 00 = 011
275
+ // 01 + 01 = 010
276
+ // 11 + 01 = 100 (!!)
277
+ // 11 + 11 = 110 (!!)
278
+ // In the last two cases, the ones we want to catch, the third bit——the
279
+ // overflow bit——is set. Bingo.
280
+ //
281
+ // We must take into account the possible carry bit from the bits
282
+ // before the discriminator. The only potentially problematic case is
283
+ // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
284
+ // bit is impossible in that case, because 00 (unset color) means the
285
+ // 24 bits that precede the discriminator are all zero.
286
+ //
287
+ // This test can be applied to both colors simultaneously.
288
+
232
289
public:
233
290
FMT_CONSTEXPR text_style (emphasis em = emphasis()) noexcept
234
- : set_foreground_color(), set_background_color(), ems(em) {}
235
-
236
- FMT_CONSTEXPR auto operator |=(const text_style& rhs) -> text_style& {
237
- if (!set_foreground_color) {
238
- set_foreground_color = rhs.set_foreground_color ;
239
- foreground_color = rhs.foreground_color ;
240
- } else if (rhs.set_foreground_color ) {
241
- if (!foreground_color.is_rgb || !rhs.foreground_color .is_rgb )
242
- report_error (" can't OR a terminal color" );
243
- foreground_color.value .rgb_color |= rhs.foreground_color .value .rgb_color ;
244
- }
291
+ : style_(static_cast <uint64_t >(em) << 54) {}
245
292
246
- if (!set_background_color) {
247
- set_background_color = rhs.set_background_color ;
248
- background_color = rhs.background_color ;
249
- } else if (rhs.set_background_color ) {
250
- if (!background_color.is_rgb || !rhs.background_color .is_rgb )
251
- report_error (" can't OR a terminal color" );
252
- background_color.value .rgb_color |= rhs.background_color .value .rgb_color ;
253
- }
254
-
255
- ems = static_cast <emphasis>(static_cast <uint8_t >(ems) |
256
- static_cast <uint8_t >(rhs.ems ));
293
+ FMT_CONSTEXPR auto operator |=(text_style rhs) -> text_style& {
294
+ if (((style_ + rhs.style_ ) & ((1ULL << 26 ) | (1ULL << 53 ))) != 0 )
295
+ report_error (" can't OR a terminal color" );
296
+ style_ |= rhs.style_ ;
257
297
return *this ;
258
298
}
259
299
260
- friend FMT_CONSTEXPR auto operator |(text_style lhs, const text_style& rhs)
300
+ friend FMT_CONSTEXPR auto operator |(text_style lhs, text_style rhs)
261
301
-> text_style {
262
302
return lhs |= rhs;
263
303
}
264
304
305
+ FMT_CONSTEXPR auto operator ==(text_style rhs) const noexcept -> bool {
306
+ return style_ == rhs.style_ ;
307
+ }
308
+
309
+ FMT_CONSTEXPR auto operator !=(text_style rhs) const noexcept -> bool {
310
+ return !(*this == rhs);
311
+ }
312
+
265
313
FMT_CONSTEXPR auto has_foreground () const noexcept -> bool {
266
- return set_foreground_color ;
314
+ return (style_ & ( 1 << 24 )) != 0 ;
267
315
}
268
316
FMT_CONSTEXPR auto has_background () const noexcept -> bool {
269
- return set_background_color ;
317
+ return (style_ & ( 1ULL << 51 )) != 0 ;
270
318
}
271
319
FMT_CONSTEXPR auto has_emphasis () const noexcept -> bool {
272
- return static_cast < uint8_t >(ems ) != 0 ;
320
+ return (style_ >> 54 ) != 0 ;
273
321
}
274
322
FMT_CONSTEXPR auto get_foreground () const noexcept -> detail::color_type {
275
323
FMT_ASSERT (has_foreground (), " no foreground specified for this style" );
276
- return foreground_color ;
324
+ return style_ & 0x3FFFFFF ;
277
325
}
278
326
FMT_CONSTEXPR auto get_background () const noexcept -> detail::color_type {
279
327
FMT_ASSERT (has_background (), " no background specified for this style" );
280
- return background_color ;
328
+ return (style_ >> 27 ) & 0x3FFFFFF ;
281
329
}
282
330
FMT_CONSTEXPR auto get_emphasis () const noexcept -> emphasis {
283
331
FMT_ASSERT (has_emphasis (), " no emphasis specified for this style" );
284
- return ems ;
332
+ return static_cast <emphasis>(style_ >> 54 ) ;
285
333
}
286
334
287
335
private:
288
- FMT_CONSTEXPR text_style (bool is_foreground,
289
- detail::color_type text_color) noexcept
290
- : set_foreground_color(), set_background_color(), ems() {
291
- if (is_foreground) {
292
- foreground_color = text_color;
293
- set_foreground_color = true ;
294
- } else {
295
- background_color = text_color;
296
- set_background_color = true ;
297
- }
298
- }
336
+ FMT_CONSTEXPR text_style (uint64_t style) noexcept : style_(style) {}
299
337
300
338
friend FMT_CONSTEXPR auto fg (detail::color_type foreground) noexcept
301
339
-> text_style;
302
340
303
341
friend FMT_CONSTEXPR auto bg (detail::color_type background) noexcept
304
342
-> text_style;
305
343
306
- detail::color_type foreground_color;
307
- detail::color_type background_color;
308
- bool set_foreground_color;
309
- bool set_background_color;
310
- emphasis ems;
344
+ uint64_t style_{};
311
345
};
312
346
313
347
// / Creates a text style from the foreground (text) color.
314
348
FMT_CONSTEXPR inline auto fg (detail::color_type foreground) noexcept
315
349
-> text_style {
316
- return text_style ( true , foreground) ;
350
+ return foreground. value_ ;
317
351
}
318
352
319
353
// / Creates a text style from the background color.
320
354
FMT_CONSTEXPR inline auto bg (detail::color_type background) noexcept
321
355
-> text_style {
322
- return text_style ( false , background) ;
356
+ return static_cast < uint64_t >( background. value_ ) << 27 ;
323
357
}
324
358
325
359
FMT_CONSTEXPR inline auto operator |(emphasis lhs, emphasis rhs) noexcept
@@ -334,9 +368,9 @@ template <typename Char> struct ansi_color_escape {
334
368
const char * esc) noexcept {
335
369
// If we have a terminal color, we need to output another escape code
336
370
// sequence.
337
- if (! text_color.is_rgb ) {
371
+ if (text_color.is_terminal_color () ) {
338
372
bool is_background = esc == string_view (" \x1b [48;2;" );
339
- uint32_t value = text_color.value . term_color ;
373
+ uint32_t value = text_color.value () ;
340
374
// Background ASCII codes are the same as the foreground ones but with
341
375
// 10 more.
342
376
if (is_background) value += 10u ;
@@ -360,7 +394,7 @@ template <typename Char> struct ansi_color_escape {
360
394
for (int i = 0 ; i < 7 ; i++) {
361
395
buffer[i] = static_cast <Char>(esc[i]);
362
396
}
363
- rgb color (text_color.value . rgb_color );
397
+ rgb color (text_color.value () );
364
398
to_esc (color.r , buffer + 7 , ' ;' );
365
399
to_esc (color.g , buffer + 11 , ' ;' );
366
400
to_esc (color.b , buffer + 15 , ' m' );
@@ -441,32 +475,26 @@ template <typename T> struct styled_arg : view {
441
475
};
442
476
443
477
template <typename Char>
444
- void vformat_to (buffer<Char>& buf, const text_style& ts,
445
- basic_string_view<Char> fmt,
478
+ void vformat_to (buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
446
479
basic_format_args<buffered_context<Char>> args) {
447
- bool has_style = false ;
448
480
if (ts.has_emphasis ()) {
449
- has_style = true ;
450
481
auto emphasis = make_emphasis<Char>(ts.get_emphasis ());
451
482
buf.append (emphasis.begin (), emphasis.end ());
452
483
}
453
484
if (ts.has_foreground ()) {
454
- has_style = true ;
455
485
auto foreground = make_foreground_color<Char>(ts.get_foreground ());
456
486
buf.append (foreground.begin (), foreground.end ());
457
487
}
458
488
if (ts.has_background ()) {
459
- has_style = true ;
460
489
auto background = make_background_color<Char>(ts.get_background ());
461
490
buf.append (background.begin (), background.end ());
462
491
}
463
492
vformat_to (buf, fmt, args);
464
- if (has_style ) reset_color<Char>(buf);
493
+ if (ts != text_style{} ) reset_color<Char>(buf);
465
494
}
466
495
} // namespace detail
467
496
468
- inline void vprint (FILE* f, const text_style& ts, string_view fmt,
469
- format_args args) {
497
+ inline void vprint (FILE* f, text_style ts, string_view fmt, format_args args) {
470
498
auto buf = memory_buffer ();
471
499
detail::vformat_to (buf, ts, fmt, args);
472
500
print (f, FMT_STRING (" {}" ), string_view (buf.begin (), buf.size ()));
@@ -482,8 +510,7 @@ inline void vprint(FILE* f, const text_style& ts, string_view fmt,
482
510
* "Elapsed time: {0:.2f} seconds", 1.23);
483
511
*/
484
512
template <typename ... T>
485
- void print (FILE* f, const text_style& ts, format_string<T...> fmt,
486
- T&&... args) {
513
+ void print (FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
487
514
vprint (f, ts, fmt.str , vargs<T...>{{args...}});
488
515
}
489
516
@@ -497,11 +524,11 @@ void print(FILE* f, const text_style& ts, format_string<T...> fmt,
497
524
* "Elapsed time: {0:.2f} seconds", 1.23);
498
525
*/
499
526
template <typename ... T>
500
- void print (const text_style& ts, format_string<T...> fmt, T&&... args) {
527
+ void print (text_style ts, format_string<T...> fmt, T&&... args) {
501
528
return print (stdout, ts, fmt, std::forward<T>(args)...);
502
529
}
503
530
504
- inline auto vformat (const text_style& ts, string_view fmt, format_args args)
531
+ inline auto vformat (text_style ts, string_view fmt, format_args args)
505
532
-> std::string {
506
533
auto buf = memory_buffer ();
507
534
detail::vformat_to (buf, ts, fmt, args);
@@ -521,16 +548,16 @@ inline auto vformat(const text_style& ts, string_view fmt, format_args args)
521
548
* ```
522
549
*/
523
550
template <typename ... T>
524
- inline auto format (const text_style& ts, format_string<T...> fmt, T&&... args)
551
+ inline auto format (text_style ts, format_string<T...> fmt, T&&... args)
525
552
-> std::string {
526
553
return fmt::vformat (ts, fmt.str , vargs<T...>{{args...}});
527
554
}
528
555
529
556
// / Formats a string with the given text_style and writes the output to `out`.
530
557
template <typename OutputIt,
531
558
FMT_ENABLE_IF (detail::is_output_iterator<OutputIt, char >::value)>
532
- auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
533
- format_args args) -> OutputIt {
559
+ auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
560
+ -> OutputIt {
534
561
auto && buf = detail::get_buffer<char >(out);
535
562
detail::vformat_to (buf, ts, fmt, args);
536
563
return detail::get_iterator (buf, out);
@@ -548,8 +575,8 @@ auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
548
575
*/
549
576
template <typename OutputIt, typename ... T,
550
577
FMT_ENABLE_IF (detail::is_output_iterator<OutputIt, char >::value)>
551
- inline auto format_to(OutputIt out, const text_style& ts,
552
- format_string<T...> fmt, T&&... args) -> OutputIt {
578
+ inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt ,
579
+ T&&... args) -> OutputIt {
553
580
return vformat_to (out, ts, fmt.str , vargs<T...>{{args...}});
554
581
}
555
582
0 commit comments