mudgangster

Tiny, scriptable MUD client
Log | Files | Refs

commit 5411db95d61852d510b826d5613f08ad44aef329
parent 2d32dfa61aff82890199a4426d33f8aed4840c8b
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Sat,  2 May 2020 21:18:33 +0300

Input cleanup

Diffstat:
Asrc/array.h | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/common.h | 108++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/input.cc | 159++++++++++++++++++++++++++++---------------------------------------------------
3 files changed, 273 insertions(+), 111 deletions(-)

diff --git a/src/array.h b/src/array.h @@ -0,0 +1,117 @@ +#pragma once + +#include "common.h" + +template< typename T > +class DynamicArray { + size_t n; + size_t capacity; + T * elems; + +public: + NONCOPYABLE( DynamicArray ); + + DynamicArray( size_t initial_capacity = 0 ) { + capacity = initial_capacity; + elems = capacity == 0 ? NULL : alloc_many< T >( capacity ); + clear(); + } + + ~DynamicArray() { + free( elems ); + } + + size_t add( const T & x ) { + size_t idx = extend( 1 ); + elems[ idx ] = x; + return idx; + } + + void insert( const T & x, size_t pos ) { + extend( 1 ); + for( size_t i = pos + 1; i < n; i++ ) { + elems[ i ] = elems[ i - 1 ]; + } + elems[ pos ] = x; + } + + void from_span( Span< const T > s ) { + resize( s.n ); + memcpy( elems, s.ptr, s.num_bytes() ); + } + + bool remove( size_t pos ) { + if( n == 0 || pos >= n ) + return false; + + n--; + for( size_t i = pos; i < n; i++ ) { + elems[ i ] = elems[ i + 1 ]; + } + + return true; + } + + void clear() { + n = 0; + } + + void resize( size_t new_size ) { + if( new_size < n ) { + n = new_size; + return; + } + + if( new_size <= capacity ) { + n = new_size; + return; + } + + size_t new_capacity = max( size_t( 64 ), capacity ); + while( new_capacity < new_size ) + new_capacity *= 2; + + elems = realloc_many< T >( elems, new_capacity ); + capacity = new_capacity; + n = new_size; + } + + size_t extend( size_t by ) { + size_t old_size = n; + resize( n + by ); + return old_size; + } + + T & operator[]( size_t i ) { + assert( i < n ); + return elems[ i ]; + } + + const T & operator[]( size_t i ) const { + assert( i < n ); + return elems[ i ]; + } + + T & top() { + assert( n > 0 ); + return elems[ n - 1 ]; + } + + const T & top() const { + assert( n > 0 ); + return elems[ n - 1 ]; + } + + T * ptr() { return elems; } + const T * ptr() const { return elems; } + size_t size() const { return n; } + size_t num_bytes() const { return sizeof( T ) * n; } + + T * begin() { return elems; } + T * end() { return elems + n; } + const T * begin() const { return elems; } + const T * end() const { return elems + n; } + + Span< T > span() { return Span< T >( elems, n ); } + Span< const T > span() const { return Span< const T >( elems, n ); } +}; diff --git a/src/common.h b/src/common.h @@ -39,9 +39,9 @@ constexpr T max( T a, T b ) { template< typename T > void swap( T & a, T & b ) { - T t = a; - a = b; - b = t; + T t = a; + a = b; + b = t; } template< typename To, typename From > @@ -51,14 +51,104 @@ inline To checked_cast( const From & from ) { return result; } +inline void * alloc_size( size_t size ) { + void * ptr = malloc( size ); + if( ptr == NULL ) { + FATAL( "malloc" ); + abort(); + } + return ptr; +} + +template< typename T > +T * alloc() { + return ( T * ) alloc_size( sizeof( T ) ); +} + template< typename T > -inline T * malloc_array( size_t count ) { - ASSERT( SIZE_MAX / count >= sizeof( T ) ); - return ( T * ) malloc( count * sizeof( T ) ); +T * alloc_many( size_t n ) { + if( SIZE_MAX / n < sizeof( T ) ) + FATAL( "allocation too large" ); + return ( T * ) alloc_size( n * sizeof( T ) ); } template< typename T > -inline T * realloc_array( T * old, size_t count ) { - ASSERT( SIZE_MAX / count >= sizeof( T ) ); - return ( T * ) realloc( old, count * sizeof( T ) ); +T * realloc_many( T * old, size_t n ) { + if( SIZE_MAX / n < sizeof( T ) ) + FATAL( "allocation too large" ); + T * res = ( T * ) realloc( old, n * sizeof( T ) ); + if( res == NULL ) { + FATAL( "realloc" ); + abort(); + } + return res; +} + +template< typename T > +struct Span { + T * ptr; + size_t n; + + constexpr Span() : ptr( NULL ), n( 0 ) { } + constexpr Span( T * ptr_, size_t n_ ) : ptr( ptr_ ), n( n_ ) { } + + // allow implicit conversion to Span< const T > + operator Span< const T >() { return Span< const T >( ptr, n ); } + operator Span< const T >() const { return Span< const T >( ptr, n ); } + + size_t num_bytes() const { return sizeof( T ) * n; } + + T & operator[]( size_t i ) { + assert( i < n ); + return ptr[ i ]; + } + + const T & operator[]( size_t i ) const { + assert( i < n ); + return ptr[ i ]; + } + + Span< T > operator+( size_t i ) { + assert( i <= n ); + return Span< T >( ptr + i, n - i ); + } + + Span< const T > operator+( size_t i ) const { + assert( i <= n ); + return Span< const T >( ptr + i, n - i ); + } + + void operator++( int ) { + assert( n > 0 ); + ptr++; + n--; + } + + T * begin() { return ptr; } + T * end() { return ptr + n; } + const T * begin() const { return ptr; } + const T * end() const { return ptr + n; } + + Span< T > slice( size_t start, size_t one_past_end ) { + assert( start <= one_past_end ); + assert( one_past_end <= n ); + return Span< T >( ptr + start, one_past_end - start ); + } + + Span< const T > slice( size_t start, size_t one_past_end ) const { + assert( start <= one_past_end ); + assert( one_past_end <= n ); + return Span< const T >( ptr + start, one_past_end - start ); + } + + template< typename S > + Span< S > cast() { + assert( num_bytes() % sizeof( S ) == 0 ); + return Span< S >( ( S * ) ptr, num_bytes() / sizeof( S ) ); + } +}; + +template< typename T > +Span< T > alloc_span( size_t n ) { + return Span< T >( alloc_many< T >( n ), n ); } diff --git a/src/input.cc b/src/input.cc @@ -2,26 +2,18 @@ #include <string.h> #include "common.h" +#include "array.h" #include "input.h" #include "script.h" #include "ui.h" -typedef struct { - char * text; - size_t len; -} InputHistory; +static Span< char > history[ MAX_INPUT_HISTORY ] = { }; +static size_t history_head = 0; +static size_t history_count = 0; +static size_t history_delta = 0; -static InputHistory inputHistory[ MAX_INPUT_HISTORY ]; -static int inputHistoryHead = 0; -static int inputHistoryCount = 0; -static int inputHistoryDelta = 0; +static DynamicArray< char > input; -static char * inputBuffer = NULL; -static char * starsBuffer = NULL; - -static size_t inputBufferSize = 256; - -static size_t inputLen = 0; static size_t cursor_pos = 0; static int left, top; @@ -30,107 +22,78 @@ static int width, height; static bool dirty = false; void input_init() { - inputBuffer = ( char * ) malloc( inputBufferSize ); - starsBuffer = ( char * ) malloc( inputBufferSize ); - - memset( starsBuffer, '*', inputBufferSize ); } void input_term() { - free( inputBuffer ); - free( starsBuffer ); } void input_return() { - if( inputLen > 0 ) { - InputHistory * lastCmd = &inputHistory[ ( inputHistoryHead + inputHistoryCount - 1 ) % MAX_INPUT_HISTORY ]; + if( input.size() > 0 ) { + Span< const char > last_cmd = history_count == 0 ? Span< const char >() : history[ ( history_head + history_count - 1 ) % MAX_INPUT_HISTORY ]; - if( inputHistoryCount == 0 || inputLen != lastCmd->len || strncmp( inputBuffer, lastCmd->text, inputLen ) != 0 ) { - int pos = ( inputHistoryHead + inputHistoryCount ) % MAX_INPUT_HISTORY; + if( history_count == 0 || input.size() != last_cmd.n || memcmp( input.ptr(), last_cmd.ptr, last_cmd.num_bytes() ) != 0 ) { + size_t pos = ( history_head + history_count ) % MAX_INPUT_HISTORY; - if( inputHistoryCount == MAX_INPUT_HISTORY ) { - free( inputHistory[ pos ].text ); - - inputHistoryHead = ( inputHistoryHead + 1 ) % MAX_INPUT_HISTORY; + if( history_count == MAX_INPUT_HISTORY ) { + history_head++; } else { - inputHistoryCount++; + history_count++; } - inputHistory[ pos ].text = ( char * ) malloc( inputLen ); - - memcpy( inputHistory[ pos ].text, inputBuffer, inputLen ); - inputHistory[ pos ].len = inputLen; + free( history[ pos ].ptr ); + history[ pos ] = alloc_span< char >( input.size() ); + memcpy( history[ pos ].ptr, input.ptr(), input.num_bytes() ); } } - script_handleInput( inputBuffer, inputLen ); - - inputHistoryDelta = 0; + script_handleInput( input.ptr(), input.size() ); - inputLen = 0; + input.clear(); + history_delta = 0; cursor_pos = 0; - dirty = true; } void input_backspace() { - if( cursor_pos > 0 ) { - memmove( inputBuffer + cursor_pos - 1, inputBuffer + cursor_pos, inputLen - cursor_pos ); + if( cursor_pos == 0 ) + return; + cursor_pos--; + input.remove( cursor_pos ); + dirty = true; +} - inputLen--; - cursor_pos--; +void input_delete() { + if( input.remove( cursor_pos ) ) { dirty = true; } } -void input_delete() { - if( cursor_pos < inputLen ) { - memmove( inputBuffer + cursor_pos, inputBuffer + cursor_pos + 1, inputLen - cursor_pos ); - - inputLen--; - dirty = true; +static void input_from_history() { + if( history_delta == 0 ) { + input.clear(); } + else { + Span< const char > cmd = history[ ( history_head + history_count - history_delta ) % MAX_INPUT_HISTORY ]; + input.from_span( cmd ); + } + + cursor_pos = input.size(); + dirty = true; } void input_up() { - if( inputHistoryDelta >= inputHistoryCount ) + if( history_delta >= history_count ) return; - - inputHistoryDelta++; - int pos = ( inputHistoryHead + inputHistoryCount - inputHistoryDelta ) % MAX_INPUT_HISTORY; - - InputHistory cmd = inputHistory[ pos ]; - - memcpy( inputBuffer, cmd.text, cmd.len ); - - inputLen = cmd.len; - cursor_pos = cmd.len; - dirty = true; + history_delta++; + input_from_history(); } void input_down() { - if( inputHistoryDelta == 0 ) + if( history_delta == 0 ) return; - - inputHistoryDelta--; - - if( inputHistoryDelta != 0 ) { - int pos = ( inputHistoryHead + inputHistoryCount - inputHistoryDelta ) % MAX_INPUT_HISTORY; - - InputHistory cmd = inputHistory[ pos ]; - - memcpy( inputBuffer, cmd.text, cmd.len ); - - inputLen = cmd.len; - cursor_pos = cmd.len; - } - else { - inputLen = 0; - cursor_pos = 0; - } - - dirty = true; + history_delta--; + input_from_history(); } void input_left() { @@ -141,29 +104,16 @@ void input_left() { } void input_right() { - cursor_pos = min( cursor_pos + 1, inputLen ); + cursor_pos = min( cursor_pos + 1, input.size() ); dirty = true; } void input_add( const char * buffer, int len ) { - if( inputLen + len >= inputBufferSize ) { - inputBufferSize *= 2; - - inputBuffer = ( char * ) realloc( inputBuffer, inputBufferSize ); - starsBuffer = ( char * ) realloc( starsBuffer, inputBufferSize ); - - memset( starsBuffer + inputBufferSize / 2, '*', inputBufferSize / 2 ); - } - - if( cursor_pos < inputLen ) { - memmove( inputBuffer + cursor_pos + len, inputBuffer + cursor_pos, inputLen - cursor_pos ); + for( int i = 0; i < len; i++ ) { + input.insert( buffer[ i ], cursor_pos ); + cursor_pos++; } - memcpy( inputBuffer + cursor_pos, buffer, len ); - - inputLen += len; - cursor_pos += len; - dirty = true; } @@ -187,14 +137,19 @@ void input_draw() { ui_fill_rect( left, top, width, height, COLOUR_BG, false ); - for( size_t i = 0; i < inputLen; i++ ) { - ui_draw_char( PADDING + i * fw, top - SPACING, inputBuffer[ i ], WHITE, false ); + size_t chars_that_fit = width / size_t( fw ); + size_t chars_to_draw = min( input.size(), chars_that_fit ); + + for( size_t i = 0; i < chars_to_draw; i++ ) { + ui_draw_char( PADDING + i * fw, top - SPACING, input[ i ], WHITE, false ); } - ui_fill_rect( PADDING + cursor_pos * fw, top, fw, fh, COLOUR_CURSOR, false ); + if( cursor_pos < chars_that_fit ) { + ui_fill_rect( PADDING + cursor_pos * fw, top, fw, fh, COLOUR_CURSOR, false ); - if( cursor_pos < inputLen ) { - ui_draw_char( PADDING + cursor_pos * fw, top - SPACING, inputBuffer[ cursor_pos ], COLOUR_BG, false ); + if( cursor_pos < input.size() ) { + ui_draw_char( PADDING + cursor_pos * fw, top - SPACING, input[ cursor_pos ], COLOUR_BG, false ); + } } dirty = false;