commit 5411db95d61852d510b826d5613f08ad44aef329
parent 2d32dfa61aff82890199a4426d33f8aed4840c8b
Author: Michael Savage <mikejsavage@gmail.com>
Date: Sat, 2 May 2020 21:18:33 +0300
Input cleanup
Diffstat:
A | src/array.h | | | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/common.h | | | 108 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
M | src/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;