Change Framebuffer's containers/methods to improve performance.

* Reduce the size of Terminal::Cell.
* Change colors and attributes in Terminal::Rendition to bitfields/bitmask.
* Change Cells to use UTF-8 strings instead of vector<wchar_t>.  Store Rows in a vector instead of a deque.
* Add various Framebuffer::append() methods for more efficient passing of single and repeated characters.
* Change title/icon strings from deques to a vector typedef-- this is more for tidiness than any real performance.
This commit is contained in:
John Hood
2014-06-14 19:16:13 -04:00
parent 8fdcdc88cd
commit 32afa96111
10 changed files with 195 additions and 164 deletions
+7 -7
View File
@@ -61,15 +61,15 @@ void ConditionalOverlayCell::apply( Framebuffer &fb, uint64_t confirmed_epoch, i
if ( unknown ) {
if ( flag && ( col != fb.ds.get_width() - 1 ) ) {
fb.get_mutable_cell( row, col )->renditions.underlined = true;
fb.get_mutable_cell( row, col )->renditions.set_attribute(Renditions::underlined, true);
}
return;
}
if ( !(*(fb.get_cell( row, col )) == replacement) ) {
if ( *fb.get_cell( row, col ) != replacement ) {
*(fb.get_mutable_cell( row, col )) = replacement;
if ( flag ) {
fb.get_mutable_cell( row, col )->renditions.underlined = true;
fb.get_mutable_cell( row, col )->renditions.set_attribute( Renditions::underlined, true );
}
}
}
@@ -275,7 +275,7 @@ void NotificationEngine::apply( Framebuffer &fb ) const
case 2: /* wide character */
this_cell = fb.get_mutable_cell( 0, overlay_col );
fb.reset_cell( this_cell );
this_cell->renditions.bold = true;
this_cell->renditions.set_attribute(Renditions::bold, true);
this_cell->renditions.foreground_color = 37;
this_cell->renditions.background_color = 44;
@@ -296,7 +296,7 @@ void NotificationEngine::apply( Framebuffer &fb ) const
overlay_col++;
}
if ( combining_cell->contents.size() < 16 ) {
if ( combining_cell->contents.size() < 32 ) {
combining_cell->contents.push_back( ch );
}
break;
@@ -345,9 +345,9 @@ void OverlayManager::apply( Framebuffer &fb )
title.apply( fb );
}
void TitleEngine::set_prefix( const wstring s )
void TitleEngine::set_prefix( const wstring &s )
{
prefix = deque<wchar_t>( s.begin(), s.end() );
prefix = Terminal::Framebuffer::title_type( s.begin(), s.end() );
}
void ConditionalOverlayRow::apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const
+3 -3
View File
@@ -310,12 +310,12 @@ namespace Overlay {
class TitleEngine {
private:
deque<wchar_t> prefix;
Terminal::Framebuffer::title_type prefix;
public:
void apply( Framebuffer &fb ) const { fb.prefix_window_title( prefix ); }
void set_prefix( const wstring s );
TitleEngine() : prefix() {}
void set_prefix( const wstring &s );
};
/* the overlay manager */
@@ -331,7 +331,7 @@ namespace Overlay {
NotificationEngine & get_notification_engine( void ) { return notifications; }
PredictionEngine & get_prediction_engine( void ) { return predictions; }
void set_title_prefix( const wstring s ) { title.set_prefix( s ); }
void set_title_prefix( const wstring &s ) { title.set_prefix( s ); }
OverlayManager() : notifications(), predictions(), title() {}
+1 -2
View File
@@ -85,8 +85,7 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
/* This function will only work in a UTF-8 locale. */
wchar_t pwc;
mbstate_t ps;
memset( &ps, 0, sizeof( ps ) );
mbstate_t ps = mbstate_t();
size_t total_bytes_parsed = 0;
size_t orig_buf_len = buf_len;
+2 -3
View File
@@ -97,7 +97,7 @@ void Emulator::print( const Parser::Print *act )
this_cell = fb.get_mutable_cell();
fb.reset_cell( this_cell );
this_cell->contents.push_back( act->ch );
this_cell->append( act->ch );
this_cell->width = chwidth;
fb.apply_renditions_to_current_cell();
@@ -127,8 +127,7 @@ void Emulator::print( const Parser::Print *act )
combining_cell->fallback = true;
fb.ds.move_col( 1, true, true );
}
if ( combining_cell->contents.size() < 16 ) {
if ( combining_cell->contents.size() < 32 ) {
/* seems like a reasonable limit on combining characters */
combining_cell->contents.push_back( act->ch );
}
+31 -27
View File
@@ -33,6 +33,7 @@
#include <stdio.h>
#include "terminaldisplay.h"
#include "terminalframebuffer.h"
using namespace Terminal;
@@ -65,7 +66,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
/* has bell been rung? */
if ( f.get_bell_count() != frame.last_frame.get_bell_count() ) {
frame.append( "\x07" );
frame.append( '\007' );
}
/* has icon name or window title changed? */
@@ -73,40 +74,38 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
( (!initialized)
|| (f.get_icon_name() != frame.last_frame.get_icon_name())
|| (f.get_window_title() != frame.last_frame.get_window_title()) ) ) {
typedef Terminal::Framebuffer::title_type title_type;
/* set icon name and window title */
if ( f.get_icon_name() == f.get_window_title() ) {
/* write combined Icon Name and Window Title */
frame.append( "\033]0;" );
const std::deque<wchar_t> &window_title( f.get_window_title() );
for ( std::deque<wchar_t>::const_iterator i = window_title.begin();
const title_type &window_title( f.get_window_title() );
for ( title_type::const_iterator i = window_title.begin();
i != window_title.end();
i++ ) {
snprintf( tmp, 64, "%lc", (wint_t)*i );
frame.append( tmp );
frame.append( *i );
}
frame.append( "\007" );
frame.append( '\007' );
/* ST is more correct, but BEL more widely supported */
} else {
/* write Icon Name */
frame.append( "\033]1;" );
const std::deque<wchar_t> &icon_name( f.get_icon_name() );
for ( std::deque<wchar_t>::const_iterator i = icon_name.begin();
const title_type &icon_name( f.get_icon_name() );
for ( title_type::const_iterator i = icon_name.begin();
i != icon_name.end();
i++ ) {
snprintf( tmp, 64, "%lc", (wint_t)*i );
frame.append( tmp );
frame.append( *i );
}
frame.append( "\007" );
frame.append( '\007' );
frame.append( "\033]2;" );
const std::deque<wchar_t> &window_title( f.get_window_title() );
for ( std::deque<wchar_t>::const_iterator i = window_title.begin();
const title_type &window_title( f.get_window_title() );
for ( title_type::const_iterator i = window_title.begin();
i != window_title.end();
i++ ) {
snprintf( tmp, 64, "%lc", (wint_t)*i );
frame.append( tmp );
frame.append( *i );
}
frame.append( "\007" );
frame.append( '\007' );
}
}
@@ -192,7 +191,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
/* scroll */
for ( int i = 0; i < lines_scrolled; i++ ) {
frame.append( "\n" );
frame.append( '\n' );
}
/* do the move in memory */
@@ -285,7 +284,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
/* have renditions changed? */
if ( (!initialized)
|| !(f.ds.get_renditions() == frame.current_rendition) ) {
frame.appendstring( f.ds.get_renditions().sgr() );
frame.append( f.ds.get_renditions().sgr() );
frame.current_rendition = f.ds.get_renditions();
}
@@ -359,7 +358,7 @@ void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &
if ( !(frame.current_rendition == cell->renditions) ) {
/* print renditions */
frame.appendstring( cell->renditions.sgr() );
frame.append( cell->renditions.sgr() );
frame.current_rendition = cell->renditions;
}
@@ -381,7 +380,7 @@ void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &
bool can_use_erase = has_bce || (cell->renditions == initial_rendition());
if ( frame.force_next_put ) {
frame.append( " " );
frame.append( ' ' );
frame.cursor_x++;
frame.x++;
frame.force_next_put = false;
@@ -404,7 +403,7 @@ void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &
frame.x += clear_count;
} else { /* no ECH, so just print a space */
/* unlike erases, this will use background color irrespective of BCE */
frame.append( " " );
frame.append( ' ' );
frame.cursor_x++;
frame.x++;
}
@@ -418,12 +417,7 @@ void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &
frame.append( "\xC2\xA0" );
}
for ( std::vector<wchar_t>::const_iterator i = cell->contents.begin();
i != cell->contents.end();
i++ ) {
snprintf( tmp, 64, "%lc", (wint_t)*i );
frame.append( tmp );
}
frame.append( cell->contents );
frame.x += cell->width;
frame.cursor_x += cell->width;
@@ -446,3 +440,13 @@ void FrameState::append_silent_move( int y, int x )
cursor_x = x;
cursor_y = y;
}
FrameState::FrameState( const Framebuffer &s_last )
: x(0), y(0),
force_next_put( false ),
str(), cursor_x(0), cursor_y(0), current_rendition( 0 ),
last_frame( s_last )
{
/* just a guess-- doesn't matter for correctness */
str.reserve( last_frame.ds.get_width() * last_frame.ds.get_height() * 4 );
}
+5 -9
View File
@@ -48,17 +48,13 @@ namespace Terminal {
Framebuffer last_frame;
FrameState( const Framebuffer &s_last )
: x(0), y(0),
force_next_put( false ),
str(), cursor_x(0), cursor_y(0), current_rendition( 0 ),
last_frame( s_last )
{
str.reserve( 1024 );
}
FrameState( const Framebuffer &s_last );
void append( const char c ) { str.append( 1, c ); }
void append( const wchar_t wc ) { Cell::append_to_str( str, wc ); }
void append( const char * s ) { str.append( s ); }
void appendstring( const std::string &s ) { str.append( s ); }
void append( const Cell::content_type &contents ) { str.append( contents.begin(), contents.end() ); }
void append_string( const std::string &append ) { str.append(append); }
void append_silent_move( int y, int x );
};
+1 -1
View File
@@ -90,7 +90,7 @@ const char *Display::ti_str( const char *capname )
}
Display::Display( bool use_environment )
: has_ech( true ), has_bce( true ), has_title( true ), posterize_colors( false ), smcup( NULL ), rmcup( NULL )
: has_ech( true ), has_bce( true ), has_title( true ), posterize_colors( 0 ), smcup( NULL ), rmcup( NULL )
{
if ( use_environment ) {
int errret = -2;
+65 -38
View File
@@ -32,17 +32,37 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "terminalframebuffer.h"
using namespace Terminal;
void Cell::reset( int background_color )
Cell::Cell( color_type background_color )
: contents(),
renditions( background_color ),
width( 1 ),
fallback( false ),
wrap( false )
{}
#if 0
Cell::Cell() /* default constructor required by C++11 STL */
: contents(),
renditions( 0 ),
width( 1 ),
fallback( false ),
wrap( false )
{
assert( false );
}
#endif
void Cell::reset( color_type background_color )
{
contents.clear();
fallback = false;
width = 1;
renditions = Renditions( background_color );
width = 1;
fallback = false;
wrap = false;
}
@@ -310,13 +330,25 @@ void Framebuffer::delete_line( int row, int count )
}
}
void Row::insert_cell( int col, int background_color )
Row::Row( size_t s_width, color_type background_color )
: cells( s_width, Cell( background_color ) )
{}
#if 0
Row::Row() /* default constructor required by C++11 STL */
: cells( 1, Cell() )
{
assert( false );
}
#endif
void Row::insert_cell( int col, color_type background_color )
{
cells.insert( cells.begin() + col, Cell( background_color ) );
cells.pop_back();
}
void Row::delete_cell( int col, int background_color )
void Row::delete_cell( int col, color_type background_color )
{
cells.push_back( Cell( background_color ) );
cells.erase( cells.begin() + col );
@@ -336,7 +368,7 @@ void Framebuffer::reset( void )
{
int width = ds.get_width(), height = ds.get_height();
ds = DrawState( width, height );
rows = std::deque<Row>( height, newrow() );
rows = rows_type( height, newrow() );
window_title.clear();
/* do not reset bell_count */
}
@@ -412,17 +444,16 @@ void DrawState::resize( int s_width, int s_height )
}
}
Renditions::Renditions( int s_background )
: bold( false ), italic( false ), underlined( false ), blink( false ),
inverse( false ), invisible( false ), foreground_color( 0 ),
background_color( s_background )
Renditions::Renditions( color_type s_background )
: foreground_color( 0 ), background_color( s_background ),
attributes( 0 )
{}
/* This routine cannot be used to set a color beyond the 16-color set. */
void Renditions::set_rendition( int num )
void Renditions::set_rendition( color_type num )
{
if ( num == 0 ) {
bold = italic = underlined = blink = inverse = invisible = false;
clear_attributes();
foreground_color = background_color = 0;
return;
}
@@ -449,13 +480,14 @@ void Renditions::set_rendition( int num )
return;
}
bool value = num < 9;
switch ( num ) {
case 1: case 22: bold = (num == 1); break;
case 3: case 23: italic = (num == 3); break;
case 4: case 24: underlined = (num == 4); break;
case 5: case 25: blink = (num == 5); break;
case 7: case 27: inverse = (num == 7); break;
case 8: case 28: invisible = (num == 8); break;
case 1: case 22: set_attribute(bold, value); break;
case 3: case 23: set_attribute(italic, value); break;
case 4: case 24: set_attribute(underlined, value); break;
case 5: case 25: set_attribute(blink, value); break;
case 7: case 27: set_attribute(inverse, value); break;
case 8: case 28: set_attribute(invisible, value); break;
}
}
@@ -478,12 +510,12 @@ std::string Renditions::sgr( void ) const
std::string ret;
ret.append( "\033[0" );
if ( bold ) ret.append( ";1" );
if ( italic ) ret.append( ";3" );
if ( underlined ) ret.append( ";4" );
if ( blink ) ret.append( ";5" );
if ( inverse ) ret.append( ";7" );
if ( invisible ) ret.append( ";8" );
if ( get_attribute( bold ) ) ret.append( ";1" );
if ( get_attribute( italic ) ) ret.append( ";3" );
if ( get_attribute( underlined ) ) ret.append( ";4" );
if ( get_attribute( blink ) ) ret.append( ";5" );
if ( get_attribute( inverse ) ) ret.append( ";7" );
if ( get_attribute( invisible ) ) ret.append( ";8" );
if ( foreground_color
&& (foreground_color <= 37) ) {
@@ -570,7 +602,7 @@ void Renditions::posterize( void )
}
}
void Row::reset( int background_color )
void Row::reset( color_type background_color )
{
for ( cells_type::iterator i = cells.begin();
i != cells.end();
@@ -579,22 +611,13 @@ void Row::reset( int background_color )
}
}
void Framebuffer::prefix_window_title( const std::deque<wchar_t> &s )
void Framebuffer::prefix_window_title( const title_type &s )
{
if ( icon_name == window_title ) {
/* preserve equivalence */
for ( std::deque<wchar_t>::const_reverse_iterator i = s.rbegin();
i != s.rend();
i++ ) {
icon_name.push_front( *i );
}
}
for ( std::deque<wchar_t>::const_reverse_iterator i = s.rbegin();
i != s.rend();
i++ ) {
window_title.push_front( *i );
icon_name.insert(icon_name.begin(), s.begin(), s.end() );
}
window_title.insert(window_title.begin(), s.begin(), s.end() );
}
wint_t Cell::debug_contents( void ) const
@@ -602,7 +625,11 @@ wint_t Cell::debug_contents( void ) const
if ( contents.empty() ) {
return '_';
} else {
return contents.front();
/* very, very cheesy */
wchar_t ch[2];
const std::string chars( contents.begin(), contents.end() );
mbstowcs(ch, chars.c_str(), 1);
return ch[0];
}
}
+78 -72
View File
@@ -33,65 +33,68 @@
#ifndef TERMINALFB_HPP
#define TERMINALFB_HPP
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <vector>
#include <deque>
#include <string>
#include <list>
#include <assert.h>
/* Terminal framebuffer */
namespace Terminal {
typedef uint16_t color_type;
class Renditions {
public:
bool bold, italic, underlined, blink, inverse, invisible;
int foreground_color;
int background_color;
typedef enum { bold, faint, italic, underlined, blink, inverse, invisible, SIZE } attribute_type;
Renditions( int s_background );
// all together, a 32 bit word now...
unsigned int foreground_color : 12;
unsigned int background_color : 12;
private:
unsigned int attributes : 8;
public:
Renditions( color_type s_background );
void set_foreground_color( int num );
void set_background_color( int num );
void set_rendition( int num );
void set_rendition( color_type num );
std::string sgr( void ) const;
void posterize( void );
bool operator==( const Renditions &x ) const
{
return (bold == x.bold) && (italic == x.italic) && (underlined == x.underlined)
&& (blink == x.blink) && (inverse == x.inverse)
&& (invisible == x.invisible) && (foreground_color == x.foreground_color)
return ( attributes == x.attributes )
&& ( foreground_color == x.foreground_color )
&& ( background_color == x.background_color );
}
void set_attribute( attribute_type attr, bool val )
{
attributes = val ?
( attributes | (1 << attr) ) :
( attributes & ~(1 << attr) );
}
bool get_attribute( attribute_type attr ) const { return attributes & ( 1 << attr ); }
void clear_attributes() { attributes = 0; }
};
class Cell {
public:
std::vector<wchar_t> contents;
char fallback; /* first character is combining character */
int width;
typedef std::string content_type; /* can be std::string, std::vector<uint8_t>, or __gnu_cxx::__vstring */
content_type contents;
Renditions renditions;
uint8_t width;
bool fallback; /* first character is combining character */
bool wrap; /* if last cell, wrap to next line */
Cell( int background_color )
: contents(),
fallback( false ),
width( 1 ),
renditions( background_color ),
wrap( false )
{}
Cell( color_type background_color );
Cell(); /* default constructor required by C++11 STL */
Cell() /* default constructor required by C++11 STL */
: contents(),
fallback( false ),
width( 1 ),
renditions( 0 ),
wrap( false )
{
assert( false );
}
void reset( int background_color );
void reset( color_type background_color );
bool operator==( const Cell &x ) const
{
@@ -102,13 +105,15 @@ namespace Terminal {
&& (wrap == x.wrap) );
}
bool operator!=( const Cell &x ) const { return !operator==( x ); }
wint_t debug_contents( void ) const;
bool is_blank( void ) const
{
return ( contents.empty()
|| ( (contents.size() == 1) && ( (contents.front() == 0x20)
|| (contents.front() == 0xA0) ) ) );
|| contents == " "
|| contents == "\xC2\xA0" );
}
bool contents_match ( const Cell &other ) const
@@ -118,6 +123,26 @@ namespace Terminal {
}
bool compare( const Cell &other ) const;
static void append_to_str( std::string &dest, const wchar_t c )
{
static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb(NULL, 0, &ps);
(void)ignore;
size_t len = wcrtomb(tmp, c, &ps);
dest.append( tmp, len );
}
void append( const wchar_t c )
{
static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb(NULL, 0, &ps);
(void)ignore;
size_t len = wcrtomb(tmp, c, &ps);
contents.insert( contents.end(), tmp, tmp+len );
}
};
class Row {
@@ -125,20 +150,13 @@ namespace Terminal {
typedef std::vector<Cell> cells_type;
cells_type cells;
Row( size_t s_width, int background_color )
: cells( s_width, Cell( background_color ) )
{}
Row( size_t s_width, color_type background_color );
Row(); /* default constructor required by C++11 STL */
Row() /* default constructor required by C++11 STL */
: cells( 1, Cell() )
{
assert( false );
}
void insert_cell( int col, color_type background_color );
void delete_cell( int col, color_type background_color );
void insert_cell( int col, int background_color );
void delete_cell( int col, int background_color );
void reset( int background_color );
void reset( color_type background_color );
bool operator==( const Row &x ) const
{
@@ -240,7 +258,7 @@ namespace Terminal {
void set_foreground_color( int x ) { renditions.set_foreground_color( x ); }
void set_background_color( int x ) { renditions.set_background_color( x ); }
void add_rendition( int x ) { renditions.set_rendition( x ); }
void add_rendition( color_type x ) { renditions.set_rendition( x ); }
Renditions get_renditions( void ) const { return renditions; }
int get_background_rendition( void ) const { return renditions.background_color; }
@@ -265,11 +283,14 @@ namespace Terminal {
};
class Framebuffer {
public:
typedef std::vector<wchar_t> title_type;
private:
typedef std::deque<Row> rows_type;
typedef std::vector<Row> rows_type;
rows_type rows;
std::deque<wchar_t> icon_name;
std::deque<wchar_t> window_title;
title_type icon_name;
title_type window_title;
unsigned int bell_count;
bool title_initialized; /* true if the window title has been set via an OSC */
@@ -289,12 +310,7 @@ namespace Terminal {
return &rows[ row ];
}
inline const Cell *get_cell( void ) const
{
return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ];
}
inline const Cell *get_cell( int row, int col ) const
inline const Cell *get_cell( int row = -1, int col = -1 ) const
{
if ( row == -1 ) row = ds.get_cursor_row();
if ( col == -1 ) col = ds.get_cursor_col();
@@ -304,22 +320,12 @@ namespace Terminal {
Row *get_mutable_row( int row )
{
if ( row == -1 ) row = ds.get_cursor_row();
return &rows[ row ];
return const_cast<Row *>(get_row( row ));
}
inline Cell *get_mutable_cell( void )
inline Cell *get_mutable_cell( int row = -1, int col = -1 )
{
return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ];
}
inline Cell *get_mutable_cell( int row, int col )
{
if ( row == -1 ) row = ds.get_cursor_row();
if ( col == -1 ) col = ds.get_cursor_col();
return &rows[ row ].cells[ col ];
return const_cast<Cell *>(get_cell( row, col ));
}
Cell *get_combining_cell( void );
@@ -337,12 +343,12 @@ namespace Terminal {
void set_title_initialized( void ) { title_initialized = true; }
bool is_title_initialized( void ) const { return title_initialized; }
void set_icon_name( const std::deque<wchar_t> &s ) { icon_name = s; }
void set_window_title( const std::deque<wchar_t> &s ) { window_title = s; }
const std::deque<wchar_t> & get_icon_name( void ) const { return icon_name; }
const std::deque<wchar_t> & get_window_title( void ) const { return window_title; }
void set_icon_name( const title_type &s ) { icon_name = s; }
void set_window_title( const title_type &s ) { window_title = s; }
const title_type & get_icon_name( void ) const { return icon_name; }
const title_type & get_window_title( void ) const { return window_title; }
void prefix_window_title( const std::deque<wchar_t> &s );
void prefix_window_title( const title_type &s );
void resize( int s_width, int s_height );
+1 -1
View File
@@ -559,7 +559,7 @@ void Dispatcher::OSC_dispatch( const Parser::OSC_End *act, Framebuffer *fb )
bool set_title = (cmd_num == 0 || cmd_num == 2);
if ( set_icon || set_title ) {
fb->set_title_initialized();
std::deque<wchar_t> newtitle( OSC_string.begin() + offset, OSC_string.end() );
Terminal::Framebuffer::title_type newtitle( OSC_string.begin() + offset, OSC_string.end() );
if ( set_icon ) { fb->set_icon_name( newtitle ); }
if ( set_title ) { fb->set_window_title( newtitle ); }