1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-29 08:32:26 +01:00

Add readv and writev for more efficient API packets (#2342)

This commit is contained in:
Otto Winter
2021-09-20 00:33:10 +02:00
committed by GitHub
parent c60c618204
commit a990898256
7 changed files with 220 additions and 84 deletions

View File

@@ -6,6 +6,10 @@
#include <cstring>
#ifdef ARDUINO_ARCH_ESP32
#include <esp_idf_version.h>
#endif
namespace esphome {
namespace socket {
@@ -76,7 +80,51 @@ class BSDSocketImpl : public Socket {
}
int listen(int backlog) override { return ::listen(fd_, backlog); }
ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); }
ssize_t readv(const struct iovec *iov, int iovcnt) override {
#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4
// esp-idf v3 doesn't have readv, emulate it
ssize_t ret = 0;
for (int i = 0; i < iovcnt; i++) {
ssize_t err = this->read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
if (err == -1) {
if (ret != 0)
// if we already read some don't return an error
break;
return err;
}
ret += err;
if (err != iov[i].iov_len)
break;
}
return ret;
#else
return ::readv(fd_, iov, iovcnt);
#endif
}
ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); }
ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); }
ssize_t writev(const struct iovec *iov, int iovcnt) override {
#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4
// esp-idf v3 doesn't have writev, emulate it
ssize_t ret = 0;
for (int i = 0; i < iovcnt; i++) {
ssize_t err =
this->send(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len, i == iovcnt - 1 ? 0 : MSG_MORE);
if (err == -1) {
if (ret != 0)
// if we already wrote some don't return an error
break;
return err;
}
ret += err;
if (err != iov[i].iov_len)
break;
}
return ret;
#else
return ::writev(fd_, iov, iovcnt);
#endif
}
int setblocking(bool blocking) override {
int fl = ::fcntl(fd_, F_GETFL, 0);
if (blocking) {

View File

@@ -81,6 +81,11 @@ struct sockaddr_storage {
};
typedef uint32_t socklen_t;
struct iovec {
void *iov_base;
size_t iov_len;
};
#ifdef ARDUINO_ARCH_ESP8266
// arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define
#ifdef INADDR_ANY
@@ -104,6 +109,7 @@ typedef uint32_t socklen_t;
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>

View File

@@ -371,7 +371,23 @@ class LWIPRawImpl : public Socket {
return read;
}
ssize_t write(const void *buf, size_t len) override {
ssize_t readv(const struct iovec *iov, int iovcnt) override {
ssize_t ret = 0;
for (int i = 0; i < iovcnt; i++) {
ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
if (err == -1) {
if (ret != 0)
// if we already read some don't return an error
break;
return err;
}
ret += err;
if (err != iov[i].iov_len)
break;
}
return ret;
}
ssize_t internal_write(const void *buf, size_t len) {
if (pcb_ == nullptr) {
errno = ECONNRESET;
return -1;
@@ -400,25 +416,60 @@ class LWIPRawImpl : public Socket {
errno = ECONNRESET;
return -1;
}
if (tcp_nagle_disabled(pcb_)) {
LWIP_LOG("tcp_output(%p)", pcb_);
err = tcp_output(pcb_);
if (err == ERR_ABRT) {
LWIP_LOG(" -> err ERR_ABRT");
// sometimes lwip returns ERR_ABRT for no apparent reason
// the connection works fine afterwards, and back with ESPAsyncTCP we
// indirectly also ignored this error
// FIXME: figure out where this is returned and what it means in this context
return to_send;
}
if (err != ERR_OK) {
LWIP_LOG(" -> err %d", err);
errno = ECONNRESET;
return -1;
}
}
return to_send;
}
int internal_output() {
LWIP_LOG("tcp_output(%p)", pcb_);
err_t err = tcp_output(pcb_);
if (err == ERR_ABRT) {
LWIP_LOG(" -> err ERR_ABRT");
// sometimes lwip returns ERR_ABRT for no apparent reason
// the connection works fine afterwards, and back with ESPAsyncTCP we
// indirectly also ignored this error
// FIXME: figure out where this is returned and what it means in this context
return 0;
}
if (err != ERR_OK) {
LWIP_LOG(" -> err %d", err);
errno = ECONNRESET;
return -1;
}
return 0;
}
ssize_t write(const void *buf, size_t len) override {
ssize_t written = internal_write(buf, len);
if (written == -1)
return -1;
if (written == 0)
// no need to output if nothing written
return 0;
int err = internal_output();
if (err == -1)
return -1;
return written;
}
ssize_t writev(const struct iovec *iov, int iovcnt) override {
ssize_t written = 0;
for (int i = 0; i < iovcnt; i++) {
ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
if (err == -1) {
if (written != 0)
// if we already read some don't return an error
break;
return err;
}
written += err;
if (err != iov[i].iov_len)
break;
}
if (written == 0)
// no need to output if nothing written
return 0;
int err = internal_output();
if (err == -1)
return -1;
return written;
}
int setblocking(bool blocking) override {
if (pcb_ == nullptr) {
errno = ECONNRESET;

View File

@@ -31,7 +31,9 @@ class Socket {
virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0;
virtual int listen(int backlog) = 0;
virtual ssize_t read(void *buf, size_t len) = 0;
virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0;
virtual ssize_t write(const void *buf, size_t len) = 0;
virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0;
virtual int setblocking(bool blocking) = 0;
virtual int loop() { return 0; };
};