From dc2237e5c1886e95d3713e2da6aac5a6de8600e9 Mon Sep 17 00:00:00 2001 From: jyelon Date: Thu, 6 Jan 2022 17:24:32 -0500 Subject: [PATCH] Make it so that drivenengine never changes (only appends) output bytes. --- luprex/core/cpp/drivenengine.cpp | 73 ++++++++++++++++---------------- luprex/core/cpp/drivenengine.hpp | 16 +++++-- luprex/core/cpp/streambuffer.cpp | 7 ++- luprex/core/cpp/streambuffer.hpp | 5 ++- 4 files changed, 59 insertions(+), 42 deletions(-) diff --git a/luprex/core/cpp/drivenengine.cpp b/luprex/core/cpp/drivenengine.cpp index 43f11e75..69426444 100644 --- a/luprex/core/cpp/drivenengine.cpp +++ b/luprex/core/cpp/drivenengine.cpp @@ -3,32 +3,33 @@ Channel::Channel(DrivenEngine *de, int chid, int port, const std::string &target, bool stop) { chid_ = chid; - sb_in_.reset(new StreamBuffer); - sb_out_.reset(new StreamBuffer); port_ = port; closed_ = false; target_ = target; - readline_enabled_ = (chid == 0); + readline_enabled_ = false; readline_lastc_ = 0; desired_prompt_ = ""; stop_driver_ = stop; + sb_in_ = std::make_shared(); + sb_out_ = std::make_shared(); + sb_drvout_ = sb_out_; } -void Channel::show_or_hide_command(bool ignore_sb_out) { - bool sb_out_empty = (sb_out_->fill() == 0) || ignore_sb_out; - if (!sb_out_empty || (!readline_enabled_) || (stop_driver_)) { - int ccsize = current_prompt_.size() + current_command_.size(); - readline_echo_ += util::repeat_string("\b \b", ccsize); +void Channel::erase_command() { + int ccsize = current_prompt_.size() + current_command_.size(); + if (ccsize > 0) { + sb_drvout_->write_bytes(util::repeat_string("\b \b", ccsize)); current_prompt_ = ""; current_command_ = ""; - return; } +} +void Channel::echo_command() { // If the prompt has changed, erase everything and start over. if (desired_prompt_ != current_prompt_) { int ccsize = current_prompt_.size() + current_command_.size(); - readline_echo_ += util::repeat_string("\b \b", ccsize); - readline_echo_ += desired_prompt_; + sb_drvout_->write_bytes(util::repeat_string("\b \b", ccsize)); + sb_drvout_->write_bytes(desired_prompt_); current_command_ = ""; current_prompt_ = desired_prompt_; } @@ -39,21 +40,20 @@ void Channel::show_or_hide_command(bool ignore_sb_out) { // Echo backspaces to remove the non-matching part. int remove = current_command_.size() - match; if (remove > 0) { - readline_echo_ += util::repeat_string("\b \b", remove); + sb_drvout_->write_bytes(util::repeat_string("\b \b", remove)); current_command_ = current_command_.substr(0, match); } // Echo the new part. std::string newpart = desired_command_.substr(current_command_.size()); if (newpart != "") { - readline_echo_ += newpart; + sb_drvout_->write_bytes(newpart); current_command_ = desired_command_; } } void Channel::set_prompt(const std::string &p) { desired_prompt_ = p; - show_or_hide_command(false); } void Channel::feed_readline(int nbytes, const char *bytes) { @@ -63,8 +63,8 @@ void Channel::feed_readline(int nbytes, const char *bytes) { // Ignore newline immediately after carriage return. // Otherwise, crlf produces two newlines. } else if ((c == '\r') || (c == '\n')) { - show_or_hide_command(true); - readline_echo_ = readline_echo_ + " \n"; + echo_command(); + sb_drvout_->write_bytes(std::string(" \n")); sb_in_->write_bytes(desired_command_); sb_in_->write_uint8('\n'); desired_command_ = ""; @@ -86,35 +86,33 @@ void Channel::feed_readline(int nbytes, const char *bytes) { } void Channel::peek_outgoing(int *nbytes, const char **bytes) { - show_or_hide_command(false); - if (readline_echo_.size() > 0) { - *nbytes = readline_echo_.size(); - *bytes = readline_echo_.c_str(); - } else { - *nbytes = sb_out_->fill(); - *bytes = sb_out_->data(); + if (readline_enabled_) { + if (!sb_out_->empty()) { + erase_command(); + sb_out_->transfer_into(sb_drvout_.get()); + } + echo_command(); } + *nbytes = sb_drvout_->fill(); + *bytes = sb_drvout_->data(); } void Channel::sent_outgoing(int nbytes) { - if (nbytes > 0) { - if (readline_echo_.size() > 0) { - if (nbytes < int(readline_echo_.size())) { - readline_echo_ = readline_echo_.substr(nbytes); - return; - } - nbytes -= readline_echo_.size(); - readline_echo_ = ""; - } - sb_out_->read_bytes(nbytes); - } + sb_drvout_->read_bytes(nbytes); } void Channel::set_readline(bool e) { if (e != readline_enabled_) { readline_enabled_ = e; + if (readline_enabled_) { + sb_drvout_ = std::make_shared(); + } else { + sb_out_->transfer_into(sb_drvout_.get()); + sb_out_->clear(); + sb_drvout_->transfer_into(sb_out_.get()); + sb_drvout_ = sb_out_; + } desired_command_ = ""; - show_or_hide_command(false); } } @@ -196,7 +194,9 @@ const std::string &DrivenEngine::drv_get_target(int chid) { } bool DrivenEngine::drv_outgoing_empty(int chid) { - return get_chid(chid)->sb_out_->empty(); + int nbytes; const char *bytes; + drv_peek_outgoing(chid, &nbytes, &bytes); + return (nbytes > 0); } bool DrivenEngine::drv_get_channel_released(int chid) { @@ -264,6 +264,7 @@ bool DrivenEngine::drv_get_stop_driver() { DrivenEngine::DrivenEngine() { next_unused_chid_ = 1; stdio_channel_ = std::make_shared(this, 0, 0, "", false); + stdio_channel_->set_readline(true); channels_[0] = stdio_channel_; rescan_lua_source_ = true; clock_ = 0.0; diff --git a/luprex/core/cpp/drivenengine.hpp b/luprex/core/cpp/drivenengine.hpp index dae1eaee..c5d45277 100644 --- a/luprex/core/cpp/drivenengine.hpp +++ b/luprex/core/cpp/drivenengine.hpp @@ -164,13 +164,22 @@ private: void feed_readline(int nbytes, const char *bytes); void peek_outgoing(int *nbytes, const char **bytes); void sent_outgoing(int nbytes); - void show_or_hide_command(bool ignore_sb_out); + void erase_command(); + void echo_command(); private: static const int READLINE_MAX=512; int chid_; - std::unique_ptr sb_in_; - std::unique_ptr sb_out_; + + // These are the in/out buffers presented to the user. + std::shared_ptr sb_in_; + std::shared_ptr sb_out_; + + // In readline mode, we inject tty echoes into the output stream. + // This buffer holds the users output interleaved with the tty echoes. + // In non-readline mode, this is just another pointer to sb_out. + std::shared_ptr sb_drvout_; + int port_; bool closed_; std::string error_; @@ -182,7 +191,6 @@ private: std::string current_command_; std::string desired_prompt_; std::string current_prompt_; - std::string readline_echo_; char readline_lastc_; bool readline_enabled_; diff --git a/luprex/core/cpp/streambuffer.cpp b/luprex/core/cpp/streambuffer.cpp index e0f4f360..84d33d9e 100644 --- a/luprex/core/cpp/streambuffer.cpp +++ b/luprex/core/cpp/streambuffer.cpp @@ -352,7 +352,7 @@ std::string StreamBuffer::read_string_limit(int64_t max_allowed) { std::string StreamBuffer::read_entire_contents() { std::string result(read_cursor_, fill()); - clear(); + read_cursor_ = write_cursor_; return result; } @@ -438,6 +438,11 @@ void StreamBuffer::copy_into(StreamBuffer *sb) { sb->write_bytes(read_cursor_, write_cursor_ - read_cursor_); } +void StreamBuffer::transfer_into(StreamBuffer *sb) { + sb->write_bytes(read_cursor_, write_cursor_ - read_cursor_); + read_cursor_ = write_cursor_; +} + bool StreamBuffer::contents_equal(const StreamBuffer *other) const { int64_t len = fill(); if (len != other->fill()) { diff --git a/luprex/core/cpp/streambuffer.hpp b/luprex/core/cpp/streambuffer.hpp index d5accff4..59011dc8 100644 --- a/luprex/core/cpp/streambuffer.hpp +++ b/luprex/core/cpp/streambuffer.hpp @@ -347,7 +347,7 @@ public: // Read the entire contents of the buffer as a string. // std::string read_entire_contents(); - + // Overwrite values previously written to the buffer. // // See the comment at the top of this file for an explanation. @@ -384,6 +384,9 @@ public: // Copy the entire contents of this streambuffer into another one. void copy_into(StreamBuffer *sb); + // Transfer the entire contents of this streambuffer into another one. + void transfer_into(StreamBuffer *sb); + // Compare the contents of this streambuffer to another one. bool contents_equal(const StreamBuffer *sb) const;