Isolated algorithmic improvements.

* Fix inefficient STL use around Parser::UTF8Parser.
* Reduce typeid() usage, change some of it to a virtual method
* Do multiple-line scrolls as a single move
This commit is contained in:
John Hood
2014-05-10 15:25:40 -04:00
parent 3fa42cb8bb
commit 8fdcdc88cd
11 changed files with 89 additions and 63 deletions
+4 -2
View File
@@ -195,9 +195,10 @@ static int vt_parser( int fd, Parser::UTF8Parser *parser )
} }
/* feed to parser */ /* feed to parser */
Parser::Actions actions;
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
std::list<Parser::Action *> actions = parser->input( buf[ i ] ); parser->input( buf[ i ], actions );
for ( std::list<Parser::Action *>::iterator j = actions.begin(); for ( Parser::Actions::iterator j = actions.begin();
j != actions.end(); j != actions.end();
j++ ) { j++ ) {
@@ -218,6 +219,7 @@ static int vt_parser( int fd, Parser::UTF8Parser *parser )
fflush( stdout ); fflush( stdout );
} }
actions.clear();
} }
return 0; return 0;
+8 -8
View File
@@ -666,9 +666,10 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
} }
last_byte = the_byte; last_byte = the_byte;
list<Parser::Action *> actions( parser.input( the_byte ) ); Parser::Actions actions;
parser.input( the_byte, actions );
for ( list<Parser::Action *>::iterator it = actions.begin(); for ( Parser::Actions::iterator it = actions.begin();
it != actions.end(); it != actions.end();
it++ ) { it++ ) {
Parser::Action *act = *it; Parser::Action *act = *it;
@@ -678,7 +679,8 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
act->name().c_str(), act->char_present ? act->ch : L'_' ); act->name().c_str(), act->char_present ? act->ch : L'_' );
*/ */
if ( typeid( *act ) == typeid( Parser::Print ) ) { const std::type_info& type_act = typeid( *act );
if ( type_act == typeid( Parser::Print ) ) {
/* make new prediction */ /* make new prediction */
init_cursor( fb ); init_cursor( fb );
@@ -809,7 +811,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
newline_carriage_return( fb ); newline_carriage_return( fb );
} }
} }
} else if ( typeid( *act ) == typeid( Parser::Execute ) ) { } else if ( type_act == typeid( Parser::Execute ) ) {
if ( act->char_present && (act->ch == 0x0d) /* CR */ ) { if ( act->char_present && (act->ch == 0x0d) /* CR */ ) {
become_tentative(); become_tentative();
newline_carriage_return( fb ); newline_carriage_return( fb );
@@ -817,10 +819,10 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
// fprintf( stderr, "Execute 0x%x\n", act->ch ); // fprintf( stderr, "Execute 0x%x\n", act->ch );
become_tentative(); become_tentative();
} }
} else if ( typeid( *act ) == typeid( Parser::Esc_Dispatch ) ) { } else if ( type_act == typeid( Parser::Esc_Dispatch ) ) {
// fprintf( stderr, "Escape sequence\n" ); // fprintf( stderr, "Escape sequence\n" );
become_tentative(); become_tentative();
} else if ( typeid( *act ) == typeid( Parser::CSI_Dispatch ) ) { } else if ( type_act == typeid( Parser::CSI_Dispatch ) ) {
if ( act->char_present && (act->ch == L'C') ) { /* right arrow */ if ( act->char_present && (act->ch == L'C') ) { /* right arrow */
init_cursor( fb ); init_cursor( fb );
if ( cursor().col < fb.ds.get_width() - 1 ) { if ( cursor().col < fb.ds.get_width() - 1 ) {
@@ -838,8 +840,6 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
// fprintf( stderr, "CSI sequence %lc\n", act->ch ); // fprintf( stderr, "CSI sequence %lc\n", act->ch );
become_tentative(); become_tentative();
} }
} else if ( typeid( *act ) == typeid( Parser::Clear ) ) {
} }
delete act; delete act;
+3 -2
View File
@@ -46,16 +46,17 @@ string Complete::act( const string &str )
{ {
for ( unsigned int i = 0; i < str.size(); i++ ) { for ( unsigned int i = 0; i < str.size(); i++ ) {
/* parse octet into up to three actions */ /* parse octet into up to three actions */
list<Action *> actions( parser.input( str[ i ] ) ); parser.input( str[ i ], actions );
/* apply actions to terminal and delete them */ /* apply actions to terminal and delete them */
for ( list<Action *>::iterator it = actions.begin(); for ( Actions::iterator it = actions.begin();
it != actions.end(); it != actions.end();
it++ ) { it++ ) {
Action *act = *it; Action *act = *it;
act->act_on_terminal( &terminal ); act->act_on_terminal( &terminal );
delete act; delete act;
} }
actions.clear();
} }
return terminal.read_octets_to_host(); return terminal.read_octets_to_host();
+6 -1
View File
@@ -48,6 +48,11 @@ namespace Terminal {
Terminal::Emulator terminal; Terminal::Emulator terminal;
Terminal::Display display; Terminal::Display display;
// Only used locally by act(), but kept here as a performance optimization,
// to avoid construction/destruction. It must always be empty
// outside calls to act() to keep horrible things from happening.
Parser::Actions actions;
typedef std::list< std::pair<uint64_t, uint64_t> > input_history_type; typedef std::list< std::pair<uint64_t, uint64_t> > input_history_type;
input_history_type input_history; input_history_type input_history;
uint64_t echo_ack; uint64_t echo_ack;
@@ -56,7 +61,7 @@ namespace Terminal {
public: public:
Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ), Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ),
input_history(), echo_ack( 0 ) {} actions(), input_history(), echo_ack( 0 ) {}
std::string act( const std::string &str ); std::string act( const std::string &str );
std::string act( const Parser::Action *act ); std::string act( const Parser::Action *act );
+6 -14
View File
@@ -41,21 +41,19 @@
const Parser::StateFamily Parser::family; const Parser::StateFamily Parser::family;
static void append_or_delete( Parser::Action *act, static void append_or_delete( Parser::Action *act,
std::list<Parser::Action *> &vec ) Parser::Actions &vec )
{ {
assert( act ); assert( act );
if ( typeid( *act ) != typeid( Parser::Ignore ) ) { if ( !act->ignore() ) {
vec.push_back( act ); vec.push_back( act );
} else { } else {
delete act; delete act;
} }
} }
std::list<Parser::Action *> Parser::Parser::input( wchar_t ch ) void Parser::Parser::input( wchar_t ch, Actions &ret )
{ {
std::list<Action *> ret;
Transition tx = state->input( ch ); Transition tx = state->input( ch );
if ( tx.next_state != NULL ) { if ( tx.next_state != NULL ) {
@@ -69,8 +67,6 @@ std::list<Parser::Action *> Parser::Parser::input( wchar_t ch )
append_or_delete( tx.next_state->enter(), ret ); append_or_delete( tx.next_state->enter(), ret );
state = tx.next_state; state = tx.next_state;
} }
return ret;
} }
Parser::UTF8Parser::UTF8Parser() Parser::UTF8Parser::UTF8Parser()
@@ -80,7 +76,7 @@ Parser::UTF8Parser::UTF8Parser()
buf[0] = '\0'; buf[0] = '\0';
} }
std::list<Parser::Action *> Parser::UTF8Parser::input( char c ) void Parser::UTF8Parser::input( char c, Actions &ret )
{ {
assert( buf_len < BUF_SIZE ); assert( buf_len < BUF_SIZE );
@@ -94,7 +90,6 @@ std::list<Parser::Action *> Parser::UTF8Parser::input( char c )
size_t total_bytes_parsed = 0; size_t total_bytes_parsed = 0;
size_t orig_buf_len = buf_len; size_t orig_buf_len = buf_len;
std::list<Action *> ret;
/* this routine is somewhat complicated in order to comply with /* this routine is somewhat complicated in order to comply with
Unicode 6.0, section 3.9, "Best Practices for using U+FFFD" */ Unicode 6.0, section 3.9, "Best Practices for using U+FFFD" */
@@ -138,7 +133,7 @@ std::list<Parser::Action *> Parser::UTF8Parser::input( char c )
/* Cast to unsigned for checks, because some /* Cast to unsigned for checks, because some
platforms (e.g. ARM) use uint32_t as wchar_t, platforms (e.g. ARM) use uint32_t as wchar_t,
causing compiler warning on "pwc > 0" check. */ causing compiler warning on "pwc > 0" check. */
uint64_t pwcheck = pwc; const uint32_t pwcheck = pwc;
if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */ if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t) 0xFFFD;
@@ -153,13 +148,10 @@ std::list<Parser::Action *> Parser::UTF8Parser::input( char c )
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t) 0xFFFD;
} }
std::list<Action *> vec = parser.input( pwc ); parser.input( pwc, ret );
ret.insert( ret.end(), vec.begin(), vec.end() );
total_bytes_parsed += bytes_parsed; total_bytes_parsed += bytes_parsed;
} }
return ret;
} }
Parser::Parser::Parser( const Parser &other ) Parser::Parser::Parser( const Parser &other )
+2 -3
View File
@@ -37,7 +37,6 @@
http://www.vt100.net/emu/dec_ansi_parser */ http://www.vt100.net/emu/dec_ansi_parser */
#include <wchar.h> #include <wchar.h>
#include <list>
#include <string.h> #include <string.h>
#include "parsertransition.h" #include "parsertransition.h"
@@ -59,7 +58,7 @@ namespace Parser {
Parser & operator=( const Parser & ); Parser & operator=( const Parser & );
~Parser() {} ~Parser() {}
std::list<Action *> input( wchar_t ch ); void input( wchar_t ch, Actions &actions );
bool operator==( const Parser &x ) const bool operator==( const Parser &x ) const
{ {
@@ -81,7 +80,7 @@ namespace Parser {
public: public:
UTF8Parser(); UTF8Parser();
std::list<Action *> input( char c ); void input( char c, Actions &actions );
bool operator==( const UTF8Parser &x ) const bool operator==( const UTF8Parser &x ) const
{ {
+2 -2
View File
@@ -43,9 +43,9 @@ std::string Action::str( void )
char thechar[ 10 ] = { 0 }; char thechar[ 10 ] = { 0 };
if ( char_present ) { if ( char_present ) {
if ( iswprint( ch ) ) if ( iswprint( ch ) )
snprintf( thechar, 10, "(%lc)", (wint_t)ch ); snprintf( thechar, 10, "(%lc)", static_cast<wint_t>(ch) );
else else
snprintf( thechar, 10, "(0x%x)", (unsigned int)ch ); snprintf( thechar, 10, "(0x%x)", static_cast<unsigned int>(ch) );
} }
return name() + std::string( thechar ); return name() + std::string( thechar );
+10 -3
View File
@@ -34,6 +34,7 @@
#define PARSERACTION_HPP #define PARSERACTION_HPP
#include <string> #include <string>
#include <vector>
namespace Terminal { namespace Terminal {
class Emulator; class Emulator;
@@ -43,8 +44,8 @@ namespace Parser {
class Action class Action
{ {
public: public:
bool char_present;
wchar_t ch; wchar_t ch;
bool char_present;
mutable bool handled; mutable bool handled;
std::string str( void ); std::string str( void );
@@ -53,14 +54,20 @@ namespace Parser {
virtual void act_on_terminal( Terminal::Emulator * ) const {}; virtual void act_on_terminal( Terminal::Emulator * ) const {};
Action() : char_present( false ), ch( -1 ), handled( false ) {}; virtual bool ignore() const { return false; }
Action() : ch( -1 ), char_present( false ), handled( false ) {};
virtual ~Action() {}; virtual ~Action() {};
virtual bool operator==( const Action &other ) const; virtual bool operator==( const Action &other ) const;
}; };
typedef std::vector<Action *> Actions;
class Ignore : public Action { class Ignore : public Action {
public: std::string name( void ) { return std::string( "Ignore" ); } public:
std::string name( void ) { return std::string( "Ignore" ); }
bool ignore() const { return true; }
}; };
class Print : public Action { class Print : public Action {
public: public:
+44 -20
View File
@@ -78,18 +78,12 @@ Framebuffer::Framebuffer( int s_width, int s_height )
void Framebuffer::scroll( int N ) void Framebuffer::scroll( int N )
{ {
if ( N >= 0 ) { if ( N >= 0 ) {
for ( int i = 0; i < N; i++ ) { delete_line( ds.get_scrolling_region_top_row(), N );
delete_line( ds.get_scrolling_region_top_row() ); ds.move_row( -N, true );
ds.move_row( -1, true );
}
} else { } else {
N = -N; N = -N;
insert_line( ds.get_scrolling_region_top_row(), N );
for ( int i = 0; i < N; i++ ) { ds.move_row( N, true );
rows.insert( rows.begin() + ds.get_scrolling_region_top_row(), newrow() );
rows.erase( rows.begin() + ds.get_scrolling_region_bottom_row() + 1 );
ds.move_row( 1, true );
}
} }
} }
@@ -258,32 +252,62 @@ void DrawState::restore_cursor( void )
new_grapheme(); new_grapheme();
} }
void Framebuffer::insert_line( int before_row ) void Framebuffer::insert_line( int before_row, int count )
{ {
if ( (before_row < ds.get_scrolling_region_top_row()) if ( (before_row < ds.get_scrolling_region_top_row())
|| (before_row > ds.get_scrolling_region_bottom_row() + 1) ) { || (before_row > ds.get_scrolling_region_bottom_row() + 1) ) {
return; return;
} }
rows.insert( rows.begin() + before_row, newrow() ); int max_scroll = ds.get_scrolling_region_bottom_row() + 1 - before_row;
rows.erase( rows.begin() + ds.get_scrolling_region_bottom_row() + 1 ); if ( count > max_scroll ) {
count = max_scroll;
} }
void Framebuffer::delete_line( int row ) if ( count == 0 ) {
return;
}
// delete old rows
rows_type::iterator start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - count;
rows.erase( start, start + count );
// insert a block of dummy rows
start = rows.begin() + before_row;
rows.insert( start, count, Row( 0, 0 ) );
// then replace with real new rows
start = rows.begin() + before_row;
for (rows_type::iterator i = start; i < start + count; i++) {
*i = newrow();
}
}
void Framebuffer::delete_line( int row, int count )
{ {
if ( (row < ds.get_scrolling_region_top_row()) if ( (row < ds.get_scrolling_region_top_row())
|| (row > ds.get_scrolling_region_bottom_row()) ) { || (row > ds.get_scrolling_region_bottom_row()) ) {
return; return;
} }
int insertbefore = ds.get_scrolling_region_bottom_row() + 1; int max_scroll = ds.get_scrolling_region_bottom_row() + 1 - row;
if ( insertbefore == ds.get_height() ) { if ( count > max_scroll ) {
rows.push_back( newrow() ); count = max_scroll;
} else {
rows.insert( rows.begin() + insertbefore, newrow() );
} }
rows.erase( rows.begin() + row ); if ( count == 0 ) {
return;
}
// delete old rows
rows_type::iterator start = rows.begin() + row;
rows.erase( start, start + count );
// insert a block of dummy rows
start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - count;
rows.insert( start, count, Row( 0, 0 ) );
// then replace with real new rows
start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - count;
for (rows_type::iterator i = start; i < start + count; i++) {
*i = newrow();
}
} }
void Row::insert_cell( int col, int background_color ) void Row::insert_cell( int col, int background_color )
+2 -2
View File
@@ -326,8 +326,8 @@ namespace Terminal {
void apply_renditions_to_current_cell( void ); void apply_renditions_to_current_cell( void );
void insert_line( int before_row ); void insert_line( int before_row, int count );
void delete_line( int row ); void delete_line( int row, int count );
void insert_cell( int row, int col ); void insert_cell( int row, int col );
void delete_cell( int row, int col ); void delete_cell( int row, int col );
+2 -6
View File
@@ -441,9 +441,7 @@ static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch )
{ {
int lines = dispatch->getparam( 0, 1 ); int lines = dispatch->getparam( 0, 1 );
for ( int i = 0; i < lines; i++ ) { fb->insert_line( fb->ds.get_cursor_row(), lines );
fb->insert_line( fb->ds.get_cursor_row() );
}
/* vt220 manual and Ecma-48 say to move to first column */ /* vt220 manual and Ecma-48 say to move to first column */
/* but xterm and gnome-terminal don't */ /* but xterm and gnome-terminal don't */
@@ -457,9 +455,7 @@ static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch )
{ {
int lines = dispatch->getparam( 0, 1 ); int lines = dispatch->getparam( 0, 1 );
for ( int i = 0; i < lines; i++ ) { fb->delete_line( fb->ds.get_cursor_row(), lines );
fb->delete_line( fb->ds.get_cursor_row() );
}
/* same story -- xterm and gnome-terminal don't /* same story -- xterm and gnome-terminal don't
move to first column */ move to first column */