From 475db750e08ebc6c76f4a05695f66c49bca3efce Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 9 Feb 2026 17:41:16 -0500 Subject: [PATCH] [uart] Change available() return type from int to size_t (#13893) Co-authored-by: Claude Opus 4.6 --- esphome/components/bl0942/bl0942.cpp | 6 +++--- esphome/components/tormatic/tormatic_cover.cpp | 2 +- esphome/components/uart/uart.h | 2 +- esphome/components/uart/uart_component.cpp | 6 +++--- esphome/components/uart/uart_component.h | 2 +- .../components/uart/uart_component_esp8266.cpp | 15 +++++++++------ esphome/components/uart/uart_component_esp8266.h | 4 ++-- .../components/uart/uart_component_esp_idf.cpp | 2 +- esphome/components/uart/uart_component_esp_idf.h | 2 +- esphome/components/uart/uart_component_host.cpp | 7 ++++--- esphome/components/uart/uart_component_host.h | 2 +- .../components/uart/uart_component_libretiny.cpp | 2 +- .../components/uart/uart_component_libretiny.h | 2 +- esphome/components/uart/uart_component_rp2040.cpp | 2 +- esphome/components/uart/uart_component_rp2040.h | 2 +- esphome/components/usb_cdc_acm/usb_cdc_acm.h | 2 +- .../components/usb_cdc_acm/usb_cdc_acm_esp32.cpp | 4 ++-- esphome/components/usb_uart/usb_uart.h | 2 +- esphome/components/weikai/weikai.cpp | 2 +- esphome/components/weikai/weikai.h | 2 +- tests/components/uart/common.h | 2 +- 21 files changed, 38 insertions(+), 34 deletions(-) diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index 95dd689b07..b408c5549c 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -46,16 +46,16 @@ static const uint32_t PKT_TIMEOUT_MS = 200; void BL0942::loop() { DataPacket buffer; - int avail = this->available(); + size_t avail = this->available(); if (!avail) { return; } - if (static_cast(avail) < sizeof(buffer)) { + if (avail < sizeof(buffer)) { if (!this->rx_start_) { this->rx_start_ = millis(); } else if (millis() > this->rx_start_ + PKT_TIMEOUT_MS) { - ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%d bytes)", avail); + ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%zu bytes)", avail); this->read_array((uint8_t *) &buffer, avail); this->rx_start_ = 0; } diff --git a/esphome/components/tormatic/tormatic_cover.cpp b/esphome/components/tormatic/tormatic_cover.cpp index ef93964a28..be412d62a8 100644 --- a/esphome/components/tormatic/tormatic_cover.cpp +++ b/esphome/components/tormatic/tormatic_cover.cpp @@ -251,7 +251,7 @@ void Tormatic::stop_at_target_() { // Read a GateStatus from the unit. The unit only sends messages in response to // status requests or commands, so a message needs to be sent first. optional Tormatic::read_gate_status_() { - if (this->available() < static_cast(sizeof(MessageHeader))) { + if (this->available() < sizeof(MessageHeader)) { return {}; } diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 72c282f1c4..bb91e5cd7c 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -43,7 +43,7 @@ class UARTDevice { return res; } - int available() { return this->parent_->available(); } + size_t available() { return this->parent_->available(); } void flush() { this->parent_->flush(); } diff --git a/esphome/components/uart/uart_component.cpp b/esphome/components/uart/uart_component.cpp index 30fc208fc9..762e56c399 100644 --- a/esphome/components/uart/uart_component.cpp +++ b/esphome/components/uart/uart_component.cpp @@ -5,13 +5,13 @@ namespace esphome::uart { static const char *const TAG = "uart"; bool UARTComponent::check_read_timeout_(size_t len) { - if (this->available() >= int(len)) + if (this->available() >= len) return true; uint32_t start_time = millis(); - while (this->available() < int(len)) { + while (this->available() < len) { if (millis() - start_time > 100) { - ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); + ESP_LOGE(TAG, "Reading from UART timed out at byte %zu!", this->available()); return false; } yield(); diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index ea6e1562f4..b6ffbbd51f 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -69,7 +69,7 @@ class UARTComponent { // Pure virtual method to return the number of bytes available for reading. // @return Number of available bytes. - virtual int available() = 0; + virtual size_t available() = 0; // Pure virtual method to block until all bytes have been written to the UART bus. virtual void flush() = 0; diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index 504d494e2e..3ebf381c84 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -206,7 +206,7 @@ bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) { #endif return true; } -int ESP8266UartComponent::available() { +size_t ESP8266UartComponent::available() { if (this->hw_serial_ != nullptr) { return this->hw_serial_->available(); } else { @@ -329,11 +329,14 @@ uint8_t ESP8266SoftwareSerial::peek_byte() { void ESP8266SoftwareSerial::flush() { // Flush is a NO-OP with software serial, all bytes are written immediately. } -int ESP8266SoftwareSerial::available() { - int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_); - if (avail < 0) - return avail + this->rx_buffer_size_; - return avail; +size_t ESP8266SoftwareSerial::available() { + // Read volatile rx_in_pos_ once to avoid TOCTOU race with ISR. + // When in >= out, data is contiguous: [out..in). + // When in < out, data wraps: [out..buf_size) + [0..in). + size_t in = this->rx_in_pos_; + if (in >= this->rx_out_pos_) + return in - this->rx_out_pos_; + return this->rx_buffer_size_ - this->rx_out_pos_ + in; } } // namespace esphome::uart diff --git a/esphome/components/uart/uart_component_esp8266.h b/esphome/components/uart/uart_component_esp8266.h index e33dd00644..e84cbe386d 100644 --- a/esphome/components/uart/uart_component_esp8266.h +++ b/esphome/components/uart/uart_component_esp8266.h @@ -23,7 +23,7 @@ class ESP8266SoftwareSerial { void write_byte(uint8_t data); - int available(); + size_t available(); protected: static void gpio_intr(ESP8266SoftwareSerial *arg); @@ -57,7 +57,7 @@ class ESP8266UartComponent : public UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; - int available() override; + size_t available() override; void flush() override; uint32_t get_config(); diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 90997787aa..19b9a4077f 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -338,7 +338,7 @@ bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { return read_len == (int32_t) length_to_read; } -int IDFUARTComponent::available() { +size_t IDFUARTComponent::available() { size_t available = 0; esp_err_t err; diff --git a/esphome/components/uart/uart_component_esp_idf.h b/esphome/components/uart/uart_component_esp_idf.h index bd6d0c792e..1ecb02d7ab 100644 --- a/esphome/components/uart/uart_component_esp_idf.h +++ b/esphome/components/uart/uart_component_esp_idf.h @@ -22,7 +22,7 @@ class IDFUARTComponent : public UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; - int available() override; + size_t available() override; void flush() override; uint8_t get_hw_serial_number() { return this->uart_num_; } diff --git a/esphome/components/uart/uart_component_host.cpp b/esphome/components/uart/uart_component_host.cpp index 69b24607d1..0e5ef3c6bd 100644 --- a/esphome/components/uart/uart_component_host.cpp +++ b/esphome/components/uart/uart_component_host.cpp @@ -265,7 +265,7 @@ bool HostUartComponent::read_array(uint8_t *data, size_t len) { return true; } -int HostUartComponent::available() { +size_t HostUartComponent::available() { if (this->file_descriptor_ == -1) { return 0; } @@ -275,9 +275,10 @@ int HostUartComponent::available() { this->update_error_(strerror(errno)); return 0; } + size_t result = available; if (this->has_peek_) - available++; - return available; + result++; + return result; }; void HostUartComponent::flush() { diff --git a/esphome/components/uart/uart_component_host.h b/esphome/components/uart/uart_component_host.h index a4a6946c0c..89b951093b 100644 --- a/esphome/components/uart/uart_component_host.h +++ b/esphome/components/uart/uart_component_host.h @@ -17,7 +17,7 @@ class HostUartComponent : public UARTComponent, public Component { void write_array(const uint8_t *data, size_t len) override; bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; - int available() override; + size_t available() override; void flush() override; void set_name(std::string port_name) { port_name_ = port_name; }; diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index 863732c88d..cb4465068d 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -169,7 +169,7 @@ bool LibreTinyUARTComponent::read_array(uint8_t *data, size_t len) { return true; } -int LibreTinyUARTComponent::available() { return this->serial_->available(); } +size_t LibreTinyUARTComponent::available() { return this->serial_->available(); } void LibreTinyUARTComponent::flush() { ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); diff --git a/esphome/components/uart/uart_component_libretiny.h b/esphome/components/uart/uart_component_libretiny.h index ec13e7da5a..31f082d31e 100644 --- a/esphome/components/uart/uart_component_libretiny.h +++ b/esphome/components/uart/uart_component_libretiny.h @@ -21,7 +21,7 @@ class LibreTinyUARTComponent : public UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; - int available() override; + size_t available() override; void flush() override; uint16_t get_config(); diff --git a/esphome/components/uart/uart_component_rp2040.cpp b/esphome/components/uart/uart_component_rp2040.cpp index 5799d26a54..0c6834055c 100644 --- a/esphome/components/uart/uart_component_rp2040.cpp +++ b/esphome/components/uart/uart_component_rp2040.cpp @@ -186,7 +186,7 @@ bool RP2040UartComponent::read_array(uint8_t *data, size_t len) { #endif return true; } -int RP2040UartComponent::available() { return this->serial_->available(); } +size_t RP2040UartComponent::available() { return this->serial_->available(); } void RP2040UartComponent::flush() { ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); diff --git a/esphome/components/uart/uart_component_rp2040.h b/esphome/components/uart/uart_component_rp2040.h index d626d11a2e..4ca58e8dc6 100644 --- a/esphome/components/uart/uart_component_rp2040.h +++ b/esphome/components/uart/uart_component_rp2040.h @@ -24,7 +24,7 @@ class RP2040UartComponent : public UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; - int available() override; + size_t available() override; void flush() override; uint16_t get_config(); diff --git a/esphome/components/usb_cdc_acm/usb_cdc_acm.h b/esphome/components/usb_cdc_acm/usb_cdc_acm.h index 065d7282d5..ddcc65232d 100644 --- a/esphome/components/usb_cdc_acm/usb_cdc_acm.h +++ b/esphome/components/usb_cdc_acm/usb_cdc_acm.h @@ -81,7 +81,7 @@ class USBCDCACMInstance : public uart::UARTComponent, public Parentedusb_rx_ringbuf_ != nullptr) { vRingbufferGetInfo(this->usb_rx_ringbuf_, nullptr, nullptr, nullptr, nullptr, &waiting); } - return static_cast(waiting) + (this->has_peek_ ? 1 : 0); + return waiting + (this->has_peek_ ? 1 : 0); } void USBCDCACMInstance::flush() { diff --git a/esphome/components/usb_uart/usb_uart.h b/esphome/components/usb_uart/usb_uart.h index 96c17bd155..94e6120457 100644 --- a/esphome/components/usb_uart/usb_uart.h +++ b/esphome/components/usb_uart/usb_uart.h @@ -97,7 +97,7 @@ class USBUartChannel : public uart::UARTComponent, public Parented(this->input_buffer_.get_available()); } + size_t available() override { return this->input_buffer_.get_available(); } void flush() override {} void check_logger_conflict() override {} void set_parity(UARTParityOptions parity) { this->parity_ = parity; } diff --git a/esphome/components/weikai/weikai.cpp b/esphome/components/weikai/weikai.cpp index d0a8f8366b..1d835daf1e 100644 --- a/esphome/components/weikai/weikai.cpp +++ b/esphome/components/weikai/weikai.cpp @@ -401,7 +401,7 @@ bool WeikaiChannel::peek_byte(uint8_t *buffer) { return this->receive_buffer_.peek(*buffer); } -int WeikaiChannel::available() { +size_t WeikaiChannel::available() { size_t available = this->receive_buffer_.count(); if (!available) available = xfer_fifo_to_buffer_(); diff --git a/esphome/components/weikai/weikai.h b/esphome/components/weikai/weikai.h index 4440d9414e..43c3a1e4f4 100644 --- a/esphome/components/weikai/weikai.h +++ b/esphome/components/weikai/weikai.h @@ -374,7 +374,7 @@ class WeikaiChannel : public uart::UARTComponent { /// @brief Returns the number of bytes in the receive buffer /// @return the number of bytes available in the receiver fifo - int available() override; + size_t available() override; /// @brief Flush the output fifo. /// @details If we refer to Serial.flush() in Arduino it says: ** Waits for the transmission of outgoing serial data diff --git a/tests/components/uart/common.h b/tests/components/uart/common.h index 5597b86410..1f9bfa15e7 100644 --- a/tests/components/uart/common.h +++ b/tests/components/uart/common.h @@ -29,7 +29,7 @@ class MockUARTComponent : public UARTComponent { MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override)); MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override)); - MOCK_METHOD(int, available, (), (override)); + MOCK_METHOD(size_t, available, (), (override)); MOCK_METHOD(void, flush, (), (override)); MOCK_METHOD(void, check_logger_conflict, (), (override)); };