From 73ed9ec7b0c0320b589fd57425e86ddbda93e23b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 17 Jun 2023 09:40:43 -0500 Subject: [PATCH] not needed --- .../espsoftwareserial/SoftwareSerial.cpp | 619 ----- components/espsoftwareserial/SoftwareSerial.h | 447 ---- .../circular_queue/Delegate.h | 2202 ----------------- .../circular_queue/MultiDelegate.h | 567 ----- .../circular_queue/circular_queue.h | 393 --- .../circular_queue/circular_queue_mp.h | 200 -- .../espsoftwareserial/circular_queue/ghostl.h | 94 - 7 files changed, 4522 deletions(-) delete mode 100644 components/espsoftwareserial/SoftwareSerial.cpp delete mode 100644 components/espsoftwareserial/SoftwareSerial.h delete mode 100644 components/espsoftwareserial/circular_queue/Delegate.h delete mode 100644 components/espsoftwareserial/circular_queue/MultiDelegate.h delete mode 100644 components/espsoftwareserial/circular_queue/circular_queue.h delete mode 100644 components/espsoftwareserial/circular_queue/circular_queue_mp.h delete mode 100644 components/espsoftwareserial/circular_queue/ghostl.h diff --git a/components/espsoftwareserial/SoftwareSerial.cpp b/components/espsoftwareserial/SoftwareSerial.cpp deleted file mode 100644 index f058954..0000000 --- a/components/espsoftwareserial/SoftwareSerial.cpp +++ /dev/null @@ -1,619 +0,0 @@ -/* - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. -Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#include "SoftwareSerial.h" -#include - -using namespace EspSoftwareSerial; - -#ifndef ESP32 -uint32_t UARTBase::m_savedPS = 0; -#else -portMUX_TYPE UARTBase::m_interruptsMux = portMUX_INITIALIZER_UNLOCKED; -#endif - -__attribute__((always_inline)) inline void IRAM_ATTR UARTBase::disableInterrupts() -{ -#ifndef ESP32 - m_savedPS = xt_rsil(15); -#else - taskENTER_CRITICAL(&m_interruptsMux); -#endif -} - -__attribute__((always_inline)) inline void IRAM_ATTR UARTBase::restoreInterrupts() -{ -#ifndef ESP32 - xt_wsr_ps(m_savedPS); -#else - taskEXIT_CRITICAL(&m_interruptsMux); -#endif -} - -constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast(0); - -UARTBase::UARTBase() { -} - -UARTBase::UARTBase(int8_t rxPin, int8_t txPin, bool invert) -{ - m_rxPin = rxPin; - m_txPin = txPin; - m_invert = invert; -} - -UARTBase::~UARTBase() { - end(); -} - -void UARTBase::setRxGPIOPinMode() { - if (m_rxValid) { - pinMode(m_rxPin, m_rxGPIOHasPullUp && m_rxGPIOPullUpEnabled ? INPUT_PULLUP : INPUT); - } -} - -void UARTBase::setTxGPIOPinMode() { - if (m_txValid) { - pinMode(m_txPin, m_txGPIOOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT); - } -} - -void UARTBase::begin(uint32_t baud, Config config, - int8_t rxPin, int8_t txPin, - bool invert) { - if (-1 != rxPin) m_rxPin = rxPin; - if (-1 != txPin) m_txPin = txPin; - m_oneWire = (m_rxPin == m_txPin); - m_invert = invert; - m_dataBits = 5 + (config & 07); - m_parityMode = static_cast(config & 070); - m_stopBits = 1 + ((config & 0300) ? 1 : 0); - m_pduBits = m_dataBits + static_cast(m_parityMode) + m_stopBits; - m_bitTicks = (microsToTicks(1000000UL) + baud / 2) / baud; - m_intTxEnabled = true; -} - -void UARTBase::beginRx(bool hasPullUp, int bufCapacity, int isrBufCapacity) { - m_rxGPIOHasPullUp = hasPullUp; - m_rxReg = portInputRegister(digitalPinToPort(m_rxPin)); - m_rxBitMask = digitalPinToBitMask(m_rxPin); - m_buffer.reset(new circular_queue((bufCapacity > 0) ? bufCapacity : 64)); - if (m_parityMode) - { - m_parityBuffer.reset(new circular_queue((m_buffer->capacity() + 7) / 8)); - m_parityInPos = m_parityOutPos = 1; - } - m_isrBuffer.reset(new circular_queue((isrBufCapacity > 0) ? - isrBufCapacity : m_buffer->capacity() * (2 + m_dataBits + static_cast(m_parityMode)))); - if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) { - m_rxValid = true; - setRxGPIOPinMode(); - } -} - -void UARTBase::beginTx() { -#if !defined(ESP8266) - m_txReg = portOutputRegister(digitalPinToPort(m_txPin)); -#endif - m_txBitMask = digitalPinToBitMask(m_txPin); - m_txValid = true; - if (!m_oneWire) { - setTxGPIOPinMode(); - digitalWrite(m_txPin, !m_invert); - } -} - -void UARTBase::end() -{ - enableRx(false); - m_txValid = false; - if (m_buffer) { - m_buffer.reset(); - } - m_parityBuffer.reset(); - if (m_isrBuffer) { - m_isrBuffer.reset(); - } -} - -uint32_t UARTBase::baudRate() { - return 1000000UL / ticksToMicros(m_bitTicks); -} - -void UARTBase::setTransmitEnablePin(int8_t txEnablePin) { - if (-1 != txEnablePin) { - m_txEnableValid = true; - m_txEnablePin = txEnablePin; - pinMode(m_txEnablePin, OUTPUT); - digitalWrite(m_txEnablePin, LOW); - } - else { - m_txEnableValid = false; - } -} - -void UARTBase::enableIntTx(bool on) { - m_intTxEnabled = on; -} - -void UARTBase::enableRxGPIOPullUp(bool on) { - m_rxGPIOPullUpEnabled = on; - setRxGPIOPinMode(); -} - -void UARTBase::enableTxGPIOOpenDrain(bool on) { - m_txGPIOOpenDrain = on; - setTxGPIOPinMode(); -} - -void UARTBase::enableTx(bool on) { - if (m_txValid && m_oneWire) { - if (on) { - enableRx(false); - setTxGPIOPinMode(); - digitalWrite(m_txPin, !m_invert); - } - else { - setRxGPIOPinMode(); - enableRx(true); - } - } -} - -void UARTBase::enableRx(bool on) { - if (m_rxValid && on != m_rxEnabled) { - if (on) { - m_rxLastBit = m_pduBits - 1; - // Init to stop bit level and current tick - m_isrLastTick = (microsToTicks(micros()) | 1) ^ m_invert; - if (m_bitTicks >= microsToTicks(1000000UL / 74880UL)) - attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitISR), this, CHANGE); - else - attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitSyncISR), this, m_invert ? RISING : FALLING); - } - else { - detachInterrupt(digitalPinToInterrupt(m_rxPin)); - } - m_rxEnabled = on; - } -} - -int UARTBase::read() { - if (!m_rxValid) { return -1; } - if (!m_buffer->available()) { - rxBits(); - if (!m_buffer->available()) { return -1; } - } - auto val = m_buffer->pop(); - if (m_parityBuffer) - { - m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; - m_parityOutPos <<= 1; - if (!m_parityOutPos) - { - m_parityOutPos = 1; - m_parityBuffer->pop(); - } - } - return val; -} - -int UARTBase::read(uint8_t* buffer, size_t size) { - if (!m_rxValid) { return 0; } - int avail; - if (0 == (avail = m_buffer->pop_n(buffer, size))) { - rxBits(); - avail = m_buffer->pop_n(buffer, size); - } - if (!avail) return 0; - if (m_parityBuffer) { - uint32_t parityBits = avail; - while (m_parityOutPos >>= 1) ++parityBits; - m_parityOutPos = (1 << (parityBits % 8)); - m_parityBuffer->pop_n(nullptr, parityBits / 8); - } - return avail; -} - -size_t UARTBase::readBytes(uint8_t* buffer, size_t size) { - if (!m_rxValid || !size) { return 0; } - size_t count = 0; - auto start = millis(); - do { - auto readCnt = read(&buffer[count], size - count); - count += readCnt; - if (count >= size) break; - if (readCnt) { - start = millis(); - } - else { - optimistic_yield(1000UL); - } - } while (millis() - start < _timeout); - return count; -} - -int UARTBase::available() { - if (!m_rxValid) { return 0; } - rxBits(); - int avail = m_buffer->available(); - if (!avail) { - optimistic_yield(10000UL); - } - return avail; -} - -void UARTBase::lazyDelay() { - // Reenable interrupts while delaying to avoid other tasks piling up - if (!m_intTxEnabled) { restoreInterrupts(); } - const auto expired = microsToTicks(micros()) - m_periodStart; - const int32_t remaining = m_periodDuration - expired; - const uint32_t ms = remaining > 0 ? ticksToMicros(remaining) / 1000UL : 0; - if (ms > 0) - { - delay(ms); - } - else - { - optimistic_yield(10000UL); - } - // Assure that below-ms part of delays are not elided - preciseDelay(); - // Disable interrupts again if applicable - if (!m_intTxEnabled) { disableInterrupts(); } -} - -void IRAM_ATTR UARTBase::preciseDelay() { - uint32_t ticks; - do { - ticks = microsToTicks(micros()); - } while ((ticks - m_periodStart) < m_periodDuration); - m_periodDuration = 0; - m_periodStart = ticks; -} - -void IRAM_ATTR UARTBase::writePeriod( - uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) { - preciseDelay(); - if (dutyCycle) - { -#if defined(ESP8266) - if (16 == m_txPin) { - GP16O = 1; - } - else { - GPOS = m_txBitMask; - } -#else - *m_txReg |= m_txBitMask; -#endif - m_periodDuration += dutyCycle; - if (offCycle || (withStopBit && !m_invert)) { - if (!withStopBit || m_invert) { - preciseDelay(); - } - else { - lazyDelay(); - } - } - } - if (offCycle) - { -#if defined(ESP8266) - if (16 == m_txPin) { - GP16O = 0; - } - else { - GPOC = m_txBitMask; - } -#else - *m_txReg &= ~m_txBitMask; -#endif - m_periodDuration += offCycle; - if (withStopBit && m_invert) lazyDelay(); - } -} - -size_t UARTBase::write(uint8_t byte) { - return write(&byte, 1); -} - -size_t UARTBase::write(uint8_t byte, Parity parity) { - return write(&byte, 1, parity); -} - -size_t UARTBase::write(const uint8_t* buffer, size_t size) { - return write(buffer, size, m_parityMode); -} - -size_t IRAM_ATTR UARTBase::write(const uint8_t* buffer, size_t size, Parity parity) { - if (m_rxValid) { rxBits(); } - if (!m_txValid) { return -1; } - - if (m_txEnableValid) { - digitalWrite(m_txEnablePin, HIGH); - } - // Stop bit: if inverted, LOW, otherwise HIGH - bool b = !m_invert; - uint32_t dutyCycle = 0; - uint32_t offCycle = 0; - if (!m_intTxEnabled) { - // Disable interrupts in order to get a clean transmit timing - disableInterrupts(); - } - const uint32_t dataMask = ((1UL << m_dataBits) - 1); - bool withStopBit = true; - m_periodDuration = 0; - m_periodStart = microsToTicks(micros()); - for (size_t cnt = 0; cnt < size; ++cnt) { - uint8_t byte = pgm_read_byte(buffer + cnt) & dataMask; - // push LSB start-data-parity-stop bit pattern into uint32_t - // Stop bits: HIGH - uint32_t word = ~0UL; - // inverted parity bit, performance tweak for xor all-bits-set word - if (parity && m_parityMode) - { - uint32_t parityBit; - switch (parity) - { - case PARITY_EVEN: - // from inverted, so use odd parity - parityBit = byte; - parityBit ^= parityBit >> 4; - parityBit &= 0xf; - parityBit = (0x9669 >> parityBit) & 1; - break; - case PARITY_ODD: - // from inverted, so use even parity - parityBit = byte; - parityBit ^= parityBit >> 4; - parityBit &= 0xf; - parityBit = (0x6996 >> parityBit) & 1; - break; - case PARITY_MARK: - parityBit = 0; - break; - case PARITY_SPACE: - // suppresses warning parityBit uninitialized - default: - parityBit = 1; - break; - } - word ^= parityBit; - } - word <<= m_dataBits; - word |= byte; - // Start bit: LOW - word <<= 1; - if (m_invert) word = ~word; - for (int i = 0; i <= m_pduBits; ++i) { - bool pb = b; - b = word & (1UL << i); - if (!pb && b) { - writePeriod(dutyCycle, offCycle, withStopBit); - withStopBit = false; - dutyCycle = offCycle = 0; - } - if (b) { - dutyCycle += m_bitTicks; - } - else { - offCycle += m_bitTicks; - } - } - withStopBit = true; - } - writePeriod(dutyCycle, offCycle, true); - if (!m_intTxEnabled) { - // restore the interrupt state if applicable - restoreInterrupts(); - } - if (m_txEnableValid) { - digitalWrite(m_txEnablePin, LOW); - } - return size; -} - -void UARTBase::flush() { - if (!m_rxValid) { return; } - m_buffer->flush(); - if (m_parityBuffer) - { - m_parityInPos = m_parityOutPos = 1; - m_parityBuffer->flush(); - } -} - -bool UARTBase::overflow() { - bool res = m_overflow; - m_overflow = false; - return res; -} - -int UARTBase::peek() { - if (!m_rxValid) { return -1; } - if (!m_buffer->available()) { - rxBits(); - if (!m_buffer->available()) return -1; - } - auto val = m_buffer->peek(); - if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; - return val; -} - -void UARTBase::rxBits() { -#ifdef ESP8266 - if (m_isrOverflow.load()) { - m_overflow = true; - m_isrOverflow.store(false); - } -#else - if (m_isrOverflow.exchange(false)) { - m_overflow = true; - } -#endif - - m_isrBuffer->for_each(m_isrBufferForEachDel); - - // A stop bit can go undetected if leading data bits are at same level - // and there was also no next start bit yet, so one word may be pending. - // Check that there was no new ISR data received in the meantime, inserting an - // extraneous stop level bit out of sequence breaks rx. - if (m_rxLastBit < m_pduBits - 1) { - const uint32_t detectionTicks = (m_pduBits - 1 - m_rxLastBit) * m_bitTicks; - if (!m_isrBuffer->available() && microsToTicks(micros()) - m_isrLastTick > detectionTicks) { - // Produce faux stop bit level, prevents start bit maldetection - // tick's LSB is repurposed for the level bit - rxBits(((m_isrLastTick + detectionTicks) | 1) ^ m_invert); - } - } -} - -void UARTBase::rxBits(const uint32_t isrTick) { - const bool level = (m_isrLastTick & 1) ^ m_invert; - - // error introduced by edge value in LSB of isrTick is negligible - uint32_t ticks = isrTick - m_isrLastTick; - m_isrLastTick = isrTick; - - uint32_t bits = ticks / m_bitTicks; - if (ticks % m_bitTicks > (m_bitTicks >> 1)) ++bits; - while (bits > 0) { - // start bit detection - if (m_rxLastBit >= (m_pduBits - 1)) { - // leading edge of start bit? - if (level) break; - m_rxLastBit = -1; - --bits; - continue; - } - // data bits - if (m_rxLastBit < (m_dataBits - 1)) { - uint8_t dataBits = min(bits, static_cast(m_dataBits - 1 - m_rxLastBit)); - m_rxLastBit += dataBits; - bits -= dataBits; - m_rxCurByte >>= dataBits; - if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); } - continue; - } - // parity bit - if (m_parityMode && m_rxLastBit == (m_dataBits - 1)) { - ++m_rxLastBit; - --bits; - m_rxCurParity = level; - continue; - } - // stop bits - // Store the received value in the buffer unless we have an overflow - // if not high stop bit level, discard word - if (bits >= static_cast(m_pduBits - 1 - m_rxLastBit) && level) { - m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits); - if (!m_buffer->push(m_rxCurByte)) { - m_overflow = true; - } - else { - if (m_parityBuffer) - { - if (m_rxCurParity) { - m_parityBuffer->pushpeek() |= m_parityInPos; - } - else { - m_parityBuffer->pushpeek() &= ~m_parityInPos; - } - m_parityInPos <<= 1; - if (!m_parityInPos) - { - m_parityBuffer->push(); - m_parityInPos = 1; - } - } - } - } - m_rxLastBit = m_pduBits - 1; - // reset to 0 is important for masked bit logic - m_rxCurByte = 0; - m_rxCurParity = false; - break; - } -} - -void IRAM_ATTR UARTBase::rxBitISR(UARTBase* self) { - const bool level = *self->m_rxReg & self->m_rxBitMask; - const uint32_t curTick = microsToTicks(micros()); - const bool empty = !self->m_isrBuffer->available(); - - // Store level and tick in the buffer unless we have an overflow - // tick's LSB is repurposed for the level bit - if (!self->m_isrBuffer->push((curTick | 1U) ^ !level)) self->m_isrOverflow.store(true); - // Trigger rx callback only when receiver is starved - if (empty) self->m_rxHandler(); -} - -void IRAM_ATTR UARTBase::rxBitSyncISR(UARTBase* self) { - bool level = self->m_invert; - const uint32_t start = microsToTicks(micros()); - uint32_t wait = self->m_bitTicks; - const bool empty = !self->m_isrBuffer->available(); - - // Store level and tick in the buffer unless we have an overflow - // tick's LSB is repurposed for the level bit - if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); - - for (uint32_t i = 0; i < self->m_pduBits; ++i) { - while (microsToTicks(micros()) - start < wait) {}; - wait += self->m_bitTicks; - - // Store level and tick in the buffer unless we have an overflow - // tick's LSB is repurposed for the level bit - if (static_cast(*self->m_rxReg & self->m_rxBitMask) != level) - { - if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true); - level = !level; - } - } - // Trigger rx callback only when receiver is starved - if (empty) self->m_rxHandler(); -} - -void UARTBase::onReceive(const Delegate& handler) { - disableInterrupts(); - m_rxHandler = handler; - restoreInterrupts(); -} - -void UARTBase::onReceive(Delegate&& handler) { - disableInterrupts(); - m_rxHandler = std::move(handler); - restoreInterrupts(); -} - -// The template member functions below must be in IRAM, but due to a bug GCC doesn't currently -// honor the attribute. Instead, it is possible to do explicit specialization and adorn -// these with the IRAM attribute: -// Delegate<>::operator (), circular_queue<>::available, -// circular_queue<>::available_for_push, circular_queue<>::push_peek, circular_queue<>::push - -template void IRAM_ATTR delegate::detail::DelegateImpl::operator()() const; -template size_t IRAM_ATTR circular_queue::available() const; -template bool IRAM_ATTR circular_queue::push(uint32_t&&); -template bool IRAM_ATTR circular_queue::push(const uint32_t&); - diff --git a/components/espsoftwareserial/SoftwareSerial.h b/components/espsoftwareserial/SoftwareSerial.h deleted file mode 100644 index 76437a8..0000000 --- a/components/espsoftwareserial/SoftwareSerial.h +++ /dev/null @@ -1,447 +0,0 @@ -/* -SoftwareSerial.h - Implementation of the Arduino software serial for ESP8266/ESP32. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. -Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef __SoftwareSerial_h -#define __SoftwareSerial_h - -#include "circular_queue/circular_queue.h" -#include - -namespace EspSoftwareSerial { - -// Interface definition for template argument of BasicUART -class IGpioCapabilities { -public: - static constexpr bool isValidPin(int8_t pin); - static constexpr bool isValidInputPin(int8_t pin); - static constexpr bool isValidOutputPin(int8_t pin); - // result is only defined for a valid Rx pin - static constexpr bool hasPullUp(int8_t pin); -}; - -class GpioCapabilities : private IGpioCapabilities { -public: - static constexpr bool isValidPin(int8_t pin) { - #if defined(ESP8266) - return (pin >= 0 && pin <= 16) && !isFlashInterfacePin(pin); - #elif defined(ESP32) - // Remove the strapping pins as defined in the datasheets, they affect bootup and other critical operations - // Remmove the flash memory pins on related devices, since using these causes memory access issues. - #ifdef CONFIG_IDF_TARGET_ESP32 - // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf, - // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32/_images/esp32-devkitC-v4-pinout.jpg - return (pin == 1) || (pin >= 3 && pin <= 5) || - (pin >= 12 && pin <= 15) || - (!psramFound() && pin >= 16 && pin <= 17) || - (pin >= 18 && pin <= 19) || - (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 39); - #elif CONFIG_IDF_TARGET_ESP32S2 - // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf, - // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/_images/esp32-s2_saola1-pinout.jpg - return (pin >= 1 && pin <= 21) || (pin >= 33 && pin <= 44); - #elif CONFIG_IDF_TARGET_ESP32C3 - // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf, - // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/_images/esp32-c3-devkitm-1-v1-pinout.jpg - return (pin >= 0 && pin <= 1) || (pin >= 3 && pin <= 7) || (pin >= 18 && pin <= 21); - #else - return pin >= 0; - #endif - #else - return pin >= 0; - #endif - } - - static constexpr bool isValidInputPin(int8_t pin) { - return isValidPin(pin) - #if defined(ESP8266) - && (pin != 16) - #endif - ; - } - - static constexpr bool isValidOutputPin(int8_t pin) { - return isValidPin(pin) - #if defined(ESP32) - #ifdef CONFIG_IDF_TARGET_ESP32 - && (pin < 34) - #elif CONFIG_IDF_TARGET_ESP32S2 - && (pin <= 45) - #elif CONFIG_IDF_TARGET_ESP32C3 - // no restrictions - #endif - #endif - ; - } - - // result is only defined for a valid Rx pin - static constexpr bool hasPullUp(int8_t pin) { - #if defined(ESP32) - return !(pin >= 34 && pin <= 39); - #else - (void)pin; - return true; - #endif - } -}; - -enum Parity : uint8_t { - PARITY_NONE = 000, - PARITY_EVEN = 020, - PARITY_ODD = 030, - PARITY_MARK = 040, - PARITY_SPACE = 070, -}; - -enum Config { - SWSERIAL_5N1 = PARITY_NONE, - SWSERIAL_6N1, - SWSERIAL_7N1, - SWSERIAL_8N1, - SWSERIAL_5E1 = PARITY_EVEN, - SWSERIAL_6E1, - SWSERIAL_7E1, - SWSERIAL_8E1, - SWSERIAL_5O1 = PARITY_ODD, - SWSERIAL_6O1, - SWSERIAL_7O1, - SWSERIAL_8O1, - SWSERIAL_5M1 = PARITY_MARK, - SWSERIAL_6M1, - SWSERIAL_7M1, - SWSERIAL_8M1, - SWSERIAL_5S1 = PARITY_SPACE, - SWSERIAL_6S1, - SWSERIAL_7S1, - SWSERIAL_8S1, - SWSERIAL_5N2 = 0200 | PARITY_NONE, - SWSERIAL_6N2, - SWSERIAL_7N2, - SWSERIAL_8N2, - SWSERIAL_5E2 = 0200 | PARITY_EVEN, - SWSERIAL_6E2, - SWSERIAL_7E2, - SWSERIAL_8E2, - SWSERIAL_5O2 = 0200 | PARITY_ODD, - SWSERIAL_6O2, - SWSERIAL_7O2, - SWSERIAL_8O2, - SWSERIAL_5M2 = 0200 | PARITY_MARK, - SWSERIAL_6M2, - SWSERIAL_7M2, - SWSERIAL_8M2, - SWSERIAL_5S2 = 0200 | PARITY_SPACE, - SWSERIAL_6S2, - SWSERIAL_7S2, - SWSERIAL_8S2, -}; - -/// This class is compatible with the corresponding AVR one, however, -/// the constructor takes no arguments, for compatibility with the -/// HardwareSerial class. -/// Instead, the begin() function handles pin assignments and logic inversion. -/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. -/// Bitrates up to at least 115200 can be used. -class UARTBase : public Stream { -public: - UARTBase(); - /// Ctor to set defaults for pins. - /// @param rxPin the GPIO pin used for RX - /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX - UARTBase(int8_t rxPin, int8_t txPin = -1, bool invert = false); - UARTBase(const UARTBase&) = delete; - UARTBase& operator= (const UARTBase&) = delete; - virtual ~UARTBase(); - /// Configure the UARTBase object for use. - /// @param baud the TX/RX bitrate - /// @param config sets databits, parity, and stop bit count - /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor - /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor - /// @param invert true: uses invert line level logic - /// @param bufCapacity the capacity for the received bytes buffer - /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous - /// bit receive buffer, a suggested size is bufCapacity times the sum of - /// start, data, parity and stop bit count. - void begin(uint32_t baud, Config config, - int8_t rxPin, int8_t txPin, bool invert); - - uint32_t baudRate(); - /// Transmit control pin. - void setTransmitEnablePin(int8_t txEnablePin); - /// Enable (default) or disable interrupts during tx. - void enableIntTx(bool on); - /// Enable (default) or disable internal rx GPIO pull-up. - void enableRxGPIOPullUp(bool on); - /// Enable or disable (default) tx GPIO output mode. - void enableTxGPIOOpenDrain(bool on); - - bool overflow(); - - int available() override; -#if defined(ESP8266) - int availableForWrite() override { -#else - int availableForWrite() { -#endif - if (!m_txValid) return 0; - return 1; - } - int peek() override; - int read() override; - /// @returns The verbatim parity bit associated with the last successful read() or peek() call - bool readParity() - { - return m_lastReadParity; - } - /// @returns The calculated bit for even parity of the parameter byte - static bool parityEven(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x6996 >> byte) & 1; - } - /// @returns The calculated bit for odd parity of the parameter byte - static bool parityOdd(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x9669 >> byte) & 1; - } - /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout - int read(uint8_t* buffer, size_t size) -#if defined(ESP8266) - override -#endif - ; - /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout - int read(char* buffer, size_t size) { - return read(reinterpret_cast(buffer), size); - } - /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through - /// Stream::setTimeout() is reached. - size_t readBytes(uint8_t* buffer, size_t size) override; - /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through - /// Stream::setTimeout() is reached. - size_t readBytes(char* buffer, size_t size) override { - return readBytes(reinterpret_cast(buffer), size); - } - void flush() override; - size_t write(uint8_t byte) override; - size_t write(uint8_t byte, Parity parity); - size_t write(const uint8_t* buffer, size_t size) override; - size_t write(const char* buffer, size_t size) { - return write(reinterpret_cast(buffer), size); - } - size_t write(const uint8_t* buffer, size_t size, Parity parity); - size_t write(const char* buffer, size_t size, Parity parity) { - return write(reinterpret_cast(buffer), size, parity); - } - operator bool() const { - return (-1 == m_rxPin || m_rxValid) && (-1 == m_txPin || m_txValid) && !(-1 == m_rxPin && m_oneWire); - } - - /// Disable or enable interrupts on the rx pin. - void enableRx(bool on); - /// One wire control. - void enableTx(bool on); - - // AVR compatibility methods. - bool listen() { enableRx(true); return true; } - void end(); - bool isListening() { return m_rxEnabled; } - bool stopListening() { enableRx(false); return true; } - - /// onReceive sets a callback that will be called in interrupt context - /// when data is received. - /// More precisely, the callback is triggered when UARTBase detects - /// a new reception, which may not yet have completed on invocation. - /// Reading - never from this interrupt context - should therefore be - /// delayed at least for the duration of one incoming word. - void onReceive(const Delegate& handler); - /// onReceive sets a callback that will be called in interrupt context - /// when data is received. - /// More precisely, the callback is triggered when UARTBase detects - /// a new reception, which may not yet have completed on invocation. - /// Reading - never from this interrupt context - should therefore be - /// delayed at least for the duration of one incoming word. - void onReceive(Delegate&& handler); - - [[deprecated("function removed; semantics of onReceive() changed; check the header file.")]] - void perform_work(); - - using Print::write; - -protected: - void beginRx(bool hasPullUp, int bufCapacity, int isrBufCapacity); - void beginTx(); - // Member variables - int8_t m_rxPin = -1; - int8_t m_txPin = -1; - bool m_invert = false; - -private: - // It's legal to exceed the deadline, for instance, - // by enabling interrupts. - void lazyDelay(); - // Synchronous precise delay - void preciseDelay(); - // If withStopBit is set, either cycle contains a stop bit. - // If dutyCycle == 0, the level is not forced to HIGH. - // If offCycle == 0, the level remains unchanged from dutyCycle. - void writePeriod( - uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); - // safely set the pin mode for the Rx GPIO pin - void setRxGPIOPinMode(); - // safely set the pin mode for the Tx GPIO pin - void setTxGPIOPinMode(); - /* check m_rxValid that calling is safe */ - void rxBits(); - void rxBits(const uint32_t isrTick); - static void disableInterrupts(); - static void restoreInterrupts(); - - static void rxBitISR(UARTBase* self); - static void rxBitSyncISR(UARTBase* self); - - static inline uint32_t IRAM_ATTR microsToTicks(uint32_t micros) __attribute__((always_inline)) { - return micros << 1; - } - static inline uint32_t ticksToMicros(uint32_t ticks) __attribute__((always_inline)) { - return ticks >> 1; - } - - // Member variables - volatile uint32_t* m_rxReg; - uint32_t m_rxBitMask; -#if !defined(ESP8266) - volatile uint32_t* m_txReg; -#endif - uint32_t m_txBitMask; - int8_t m_txEnablePin = -1; - uint8_t m_dataBits; - bool m_oneWire; - bool m_rxValid = false; - bool m_rxEnabled = false; - bool m_txValid = false; - bool m_txEnableValid = false; - /// PDU bits include data, parity and stop bits; the start bit is not counted. - uint8_t m_pduBits; - bool m_intTxEnabled; - bool m_rxGPIOHasPullUp = false; - bool m_rxGPIOPullUpEnabled = true; - bool m_txGPIOOpenDrain = false; - Parity m_parityMode; - uint8_t m_stopBits; - bool m_lastReadParity; - bool m_overflow = false; - uint32_t m_bitTicks; - uint8_t m_parityInPos; - uint8_t m_parityOutPos; - int8_t m_rxLastBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit. - uint8_t m_rxCurByte = 0; - std::unique_ptr > m_buffer; - std::unique_ptr > m_parityBuffer; - uint32_t m_periodStart; - uint32_t m_periodDuration; -#ifndef ESP32 - static uint32_t m_savedPS; -#else - static portMUX_TYPE m_interruptsMux; -#endif - // the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement): - // 1 = positive including 0, 0 = negative. - std::unique_ptr > m_isrBuffer; - const Delegate m_isrBufferForEachDel { [](UARTBase* self, uint32_t&& isrTick) { self->rxBits(isrTick); }, this }; - std::atomic m_isrOverflow { false }; - uint32_t m_isrLastTick; - bool m_rxCurParity = false; - Delegate m_rxHandler; -}; - -template< class GpioCapabilities > class BasicUART : public UARTBase { - static_assert(std::is_base_of::value, - "template argument is not derived from IGpioCapabilities"); -public: - BasicUART() : UARTBase() { - } - /// Ctor to set defaults for pins. - /// @param rxPin the GPIO pin used for RX - /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX - BasicUART(int8_t rxPin, int8_t txPin = -1, bool invert = false) : - UARTBase(rxPin, txPin, invert) { - } - - /// Configure the BasicUART object for use. - /// @param baud the TX/RX bitrate - /// @param config sets databits, parity, and stop bit count - /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor - /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor - /// @param invert true: uses invert line level logic - /// @param bufCapacity the capacity for the received bytes buffer - /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous - /// bit receive buffer, a suggested size is bufCapacity times the sum of - /// start, data, parity and stop bit count. - void begin(uint32_t baud, Config config, - int8_t rxPin, int8_t txPin, bool invert, - int bufCapacity = 64, int isrBufCapacity = 0) { - UARTBase::begin(baud, config, rxPin, txPin, invert); - if (GpioCapabilities::isValidInputPin(rxPin)) { - beginRx(GpioCapabilities:: hasPullUp(rxPin), bufCapacity, isrBufCapacity); - } - if (GpioCapabilities::isValidOutputPin(txPin)) { - beginTx(); - } - enableRx(true); - } - void begin(uint32_t baud, Config config, - int8_t rxPin, int8_t txPin) { - begin(baud, config, rxPin, txPin, m_invert); - } - void begin(uint32_t baud, Config config, - int8_t rxPin) { - begin(baud, config, rxPin, m_txPin, m_invert); - } - void begin(uint32_t baud, Config config = SWSERIAL_8N1) { - begin(baud, config, m_rxPin, m_txPin, m_invert); - } - void setTransmitEnablePin(int8_t txEnablePin) { - UARTBase::setTransmitEnablePin( - GpioCapabilities::isValidOutputPin(txEnablePin) ? txEnablePin : -1); - } -}; - -using UART = BasicUART< GpioCapabilities >; - -}; // namespace EspSoftwareSerial - -using SoftwareSerial = EspSoftwareSerial::UART; -using namespace EspSoftwareSerial; - -// The template member functions below must be in IRAM, but due to a bug GCC doesn't currently -// honor the attribute. Instead, it is possible to do explicit specialization and adorn -// these with the IRAM attribute: -// Delegate<>::operator (), circular_queue<>::available, -// circular_queue<>::available_for_push, circular_queue<>::push_peek, circular_queue<>::push - -extern template void delegate::detail::DelegateImpl::operator()() const; -extern template size_t circular_queue::available() const; -extern template bool circular_queue::push(uint32_t&&); -extern template bool circular_queue::push(const uint32_t&); - -#endif // __SoftwareSerial_h - diff --git a/components/espsoftwareserial/circular_queue/Delegate.h b/components/espsoftwareserial/circular_queue/Delegate.h deleted file mode 100644 index 461287e..0000000 --- a/components/espsoftwareserial/circular_queue/Delegate.h +++ /dev/null @@ -1,2202 +0,0 @@ -/* -Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __Delegate_h -#define __Delegate_h - -#if defined(ESP8266) -#include -#elif defined(ESP32) -#include -#else -#define IRAM_ATTR -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) -#include -#include -#else -#include "circular_queue/ghostl.h" -#endif - -namespace -{ - - template - __attribute__((always_inline)) inline R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) - { - using target_type = R(P...); - return reinterpret_cast(fn)(std::forward(args...)); - } - -} - -namespace delegate -{ - namespace detail - { - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); - using FunctionType = std::function; - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegatePImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - else if (FPA == kind) - obj.~A(); - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(std::move(del.obj)); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - new (&this->obj) A(obj); - } - - DelegatePImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - new (&this->obj) A(std::move(obj)); - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = del.functional; - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - fn = nullptr; - return *this; - } - - IRAM_ATTR operator bool() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return fnA; - } - else - { - return functional ? true : false; - } - } - - static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) __attribute__((always_inline)) - { - return static_cast(self)->fnA( - static_cast(self)->obj, - std::forward(args...)); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else if (FPA == kind) - { - return vPtrToFunAPtrExec; - } - else - { - return [](void* self, P... args) -> R - { - return static_cast(self)->functional(std::forward(args...)); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return [this](P... args) { return fnA(obj, std::forward(args...)); }; - } - else - { - return functional; - } - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - if (fn) return fn(std::forward(args...)); - } - else if (FPA == kind) - { - if (fnA) return fnA(obj, std::forward(args...)); - } - else - { - if (functional) return functional(std::forward(args...)); - } - return R(); - } - - protected: - union { - FunctionType functional; - FunPtr fn; - struct { - FunAPtr fnA; - A obj; - }; - }; - enum { FUNC, FP, FPA } kind; - }; -#else - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - this->obj = obj; - } - - DelegatePImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - this->obj = std::move(obj); - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FP; - fn = std::forward(functional); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - fn = nullptr; - return *this; - } - - IRAM_ATTR operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return fnA; - } - } - - static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) __attribute__((always_inline)) - { - return static_cast(self)->fnA( - static_cast(self)->obj, - std::forward(args...)); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else - { - return vPtrToFunAPtrExec; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - if (fn) return fn(std::forward(args...)); - } - else - { - if (fnA) return fnA(obj, std::forward(args...)); - } - return R(); - } - - protected: - union { - FunPtr fn; - FunAPtr fnA; - }; - A obj; - enum { FP, FPA } kind; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunctionType = std::function; - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegatePImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = del.functional; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - kind = FP; - } - DelegatePImpl::fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - kind = FP; - fn = nullptr; - return *this; - } - - IRAM_ATTR operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional ? true : false; - } - } - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else - { - return [](void* self, P... args) -> R - { - return static_cast(self)->functional(std::forward(args...)); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional; - } - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - if (fn) return fn(std::forward(args...)); - } - else - { - if (functional) return functional(std::forward(args...)); - } - return R(); - } - - protected: - union { - FunctionType functional; - FunPtr fn; - }; - enum { FUNC, FP } kind; - }; -#else - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - fn = nullptr; - } - - DelegatePImpl(const DelegatePImpl& del) - { - fn = del.fn; - } - - DelegatePImpl(DelegatePImpl&& del) - { - fn = std::move(del.fn); - } - - DelegatePImpl(FunPtr fn) - { - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F fn) - { - DelegatePImpl::fn = std::forward(fn); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - fn = del.fn; - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - fn = std::move(del.fn); - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - DelegatePImpl::fn = fn; - return *this; - } - - inline DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) - { - fn = nullptr; - return *this; - } - - inline IRAM_ATTR operator bool() const __attribute__((always_inline)) - { - return fn; - } - - operator FunVPPtr() const - { - return vPtrToFunPtrExec; - } - - void* arg() const - { - return reinterpret_cast(fn); - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - inline R IRAM_ATTR operator()(P... args) const __attribute__((always_inline)) - { - if (fn) return fn(std::forward(args...)); - return R(); - } - - protected: - FunPtr fn; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunctionType = std::function; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegateImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - else if (FPA == kind) - obj.~A(); - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(std::move(del.obj)); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - new (&this->obj) A(obj); - } - - DelegateImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - new (&this->obj) A(std::move(obj)); - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = del.functional; - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - fn = nullptr; - return *this; - } - - IRAM_ATTR operator bool() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return fnA; - } - else - { - return functional ? true : false; - } - } - - static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self) __attribute__((always_inline)) - { - return static_cast(self)->fnA( - static_cast(self)->obj); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else if (FPA == kind) - { - return vPtrToFunAPtrExec; - } - else - { - return [](void* self) -> R - { - return static_cast(self)->functional(); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return [this]() { return fnA(obj); }; - } - else - { - return functional; - } - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - if (fn) return fn(); - } - else if (FPA == kind) - { - if (fnA) return fnA(obj); - } - else - { - if (functional) return functional(); - } - return R(); - } - - protected: - union { - FunctionType functional; - FunPtr fn; - struct { - FunAPtr fnA; - A obj; - }; - }; - enum { FUNC, FP, FPA } kind; - }; -#else - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - this->obj = obj; - } - - DelegateImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - this->obj = std::move(obj); - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F fn) - { - kind = FP; - DelegateImpl::fn = std::forward(fn); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - fn = nullptr; - return *this; - } - - IRAM_ATTR operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return fnA; - } - } - - static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self) __attribute__((always_inline)) - { - return static_cast(self)->fnA( - static_cast(self)->obj); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return vPtrToFunAPtrExec; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - if (fn) return fn(); - } - else - { - if (fnA) return fnA(obj); - } - return R(); - } - - protected: - union { - FunPtr fn; - FunAPtr fnA; - }; - A obj; - enum { FP, FPA } kind; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunctionType = std::function; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegateImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = del.functional; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - kind = FP; - } - DelegateImpl::fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - kind = FP; - fn = nullptr; - return *this; - } - - IRAM_ATTR operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional ? true : false; - } - } - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return [](void* self) -> R - { - return static_cast(self)->functional(); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional; - } - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - if (fn) return fn(); - } - else - { - if (functional) return functional(); - } - return R(); - } - - protected: - union { - FunctionType functional; - FunPtr fn; - }; - enum { FUNC, FP } kind; - }; -#else - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - fn = nullptr; - } - - DelegateImpl(const DelegateImpl& del) - { - fn = del.fn; - } - - DelegateImpl(DelegateImpl&& del) - { - fn = std::move(del.fn); - } - - DelegateImpl(FunPtr fn) - { - DelegateImpl::fn = fn; - } - - template DelegateImpl(F fn) - { - DelegateImpl::fn = std::forward(fn); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - fn = del.fn; - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - fn = std::move(del.fn); - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - DelegateImpl::fn = fn; - return *this; - } - - inline DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) - { - fn = nullptr; - return *this; - } - - inline IRAM_ATTR operator bool() const __attribute__((always_inline)) - { - return fn; - } - - operator FunVPPtr() const - { - return reinterpret_cast(fn); - } - - void* arg() const - { - return nullptr; - } - - /// Calling is safe without checking for nullptr. - /// If non-void, returns the default value. - /// In ISR context, where faults and exceptions must not - /// occurs, this saves the extra check for nullptr, - /// and allows the compiler to optimize out checks - /// in std::function which may not be ISR-safe or - /// cause linker errors, like l32r relocation errors - /// on the Xtensa ISA. - inline R IRAM_ATTR operator()() const __attribute__((always_inline)) - { - if (fn) return fn(); - return R(); - } - - protected: - FunPtr fn; - }; -#endif - - template - class Delegate : private detail::DelegatePImpl - { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using FunctionType = std::function; -#endif - public: - using detail::DelegatePImpl::operator bool; - using detail::DelegatePImpl::arg; - using detail::DelegatePImpl::operator(); - - operator FunVPPtr() { return detail::DelegatePImpl::operator FunVPPtr(); } -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } -#endif - - Delegate() : detail::DelegatePImpl::DelegatePImpl() {} - - Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} - - Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( - std::move(static_cast&>(del))) {} - - Delegate(FunAPtr fnA, const A& obj) : detail::DelegatePImpl::DelegatePImpl(fnA, obj) {} - - Delegate(FunAPtr fnA, A&& obj) : detail::DelegatePImpl::DelegatePImpl(fnA, std::move(obj)) {} - - Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} - - template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - detail::DelegatePImpl::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - detail::DelegatePImpl::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(FunPtr fn) { - detail::DelegatePImpl::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - detail::DelegatePImpl::operator=(nullptr); - return *this; - } - }; - - template - class Delegate : private detail::DelegatePImpl - { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A*, P...); - using FunVPPtr = R(*)(void*, P...); -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using FunctionType = std::function; -#endif - public: - using detail::DelegatePImpl::operator bool; - using detail::DelegatePImpl::operator(); - - operator FunVPPtr() const - { - if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) - { - return reinterpret_cast(detail::DelegatePImpl::fnA); - } - else - { - return detail::DelegatePImpl::operator FunVPPtr(); - } - } -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } -#endif - void* arg() const - { - if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) - { - return detail::DelegatePImpl::obj; - } - else - { - return detail::DelegatePImpl::arg(); - } - } - - Delegate() : detail::DelegatePImpl::DelegatePImpl() {} - - Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} - - Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( - std::move(static_cast&>(del))) {} - - Delegate(FunAPtr fnA, A* obj) : detail::DelegatePImpl::DelegatePImpl(fnA, obj) {} - - Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} - - template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - detail::DelegatePImpl::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - detail::DelegatePImpl::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(FunPtr fn) { - detail::DelegatePImpl::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - detail::DelegatePImpl::operator=(nullptr); - return *this; - } - }; - - template - class Delegate : private detail::DelegatePImpl - { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using FunctionType = std::function; -#endif - using FunVPPtr = R(*)(void*, P...); - public: - using detail::DelegatePImpl::operator bool; - using detail::DelegatePImpl::arg; - using detail::DelegatePImpl::operator(); - - operator FunVPPtr() const { return detail::DelegatePImpl::operator FunVPPtr(); } -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } -#endif - - Delegate() : detail::DelegatePImpl::DelegatePImpl() {} - - Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} - - Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( - std::move(static_cast&>(del))) {} - - Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} - - template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - detail::DelegatePImpl::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - detail::DelegatePImpl::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(FunPtr fn) { - detail::DelegatePImpl::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - detail::DelegatePImpl::operator=(nullptr); - return *this; - } - }; - - template - class Delegate : private detail::DelegateImpl - { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunVPPtr = R(*)(void*); -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using FunctionType = std::function; -#endif - public: - using detail::DelegateImpl::operator bool; - using detail::DelegateImpl::arg; - using detail::DelegateImpl::operator(); - - operator FunVPPtr() { return detail::DelegateImpl::operator FunVPPtr(); } -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } -#endif - - Delegate() : detail::DelegateImpl::DelegateImpl() {} - - Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} - - Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( - std::move(static_cast&>(del))) {} - - Delegate(FunAPtr fnA, const A& obj) : detail::DelegateImpl::DelegateImpl(fnA, obj) {} - - Delegate(FunAPtr fnA, A&& obj) : detail::DelegateImpl::DelegateImpl(fnA, std::move(obj)) {} - - Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} - - template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - detail::DelegateImpl::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - detail::DelegateImpl::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(FunPtr fn) { - detail::DelegateImpl::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - detail::DelegateImpl::operator=(nullptr); - return *this; - } - }; - - template - class Delegate : private detail::DelegateImpl - { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A*); - using FunVPPtr = R(*)(void*); -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using FunctionType = std::function; -#endif - public: - using detail::DelegateImpl::operator bool; - using detail::DelegateImpl::operator(); - - operator FunVPPtr() const - { - if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) - { - return reinterpret_cast(detail::DelegateImpl::fnA); - } - else - { - return detail::DelegateImpl::operator FunVPPtr(); - } - } -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } -#endif - void* arg() const - { - if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) - { - return detail::DelegateImpl::obj; - } - else - { - return detail::DelegateImpl::arg(); - } - } - - Delegate() : detail::DelegateImpl::DelegateImpl() {} - - Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} - - Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( - std::move(static_cast&>(del))) {} - - Delegate(FunAPtr fnA, A* obj) : detail::DelegateImpl::DelegateImpl(fnA, obj) {} - - Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} - - template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - detail::DelegateImpl::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - detail::DelegateImpl::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(FunPtr fn) { - detail::DelegateImpl::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - detail::DelegateImpl::operator=(nullptr); - return *this; - } - }; - - template - class Delegate : private detail::DelegateImpl - { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using FunctionType = std::function; -#endif - using FunVPPtr = R(*)(void*); - public: - using detail::DelegateImpl::operator bool; - using detail::DelegateImpl::arg; - using detail::DelegateImpl::operator(); - - operator FunVPPtr() const { return detail::DelegateImpl::operator FunVPPtr(); } -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } -#endif - - Delegate() : detail::DelegateImpl::DelegateImpl() {} - - Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} - - Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( - std::move(static_cast&>(del))) {} - - Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} - - template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - detail::DelegateImpl::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - detail::DelegateImpl::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(FunPtr fn) { - detail::DelegateImpl::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - detail::DelegateImpl::operator=(nullptr); - return *this; - } - }; - } -} - -template class Delegate; -template class Delegate : public delegate::detail::Delegate -{ -public: - Delegate() : delegate::detail::Delegate::Delegate() {} - - Delegate(std::nullptr_t) : delegate::detail::Delegate::Delegate(nullptr) {} - - Delegate(const Delegate& del) : delegate::detail::Delegate::Delegate( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : delegate::detail::Delegate::Delegate( - std::move(static_cast&>(del))) {} - - Delegate(typename delegate::detail::Delegate::FunAPtr fnA, const A& obj) : delegate::detail::Delegate::Delegate(fnA, obj) {} - - Delegate(typename delegate::detail::Delegate::FunAPtr fnA, A&& obj) : delegate::detail::Delegate::Delegate(fnA, std::move(obj)) {} - - Delegate(typename delegate::detail::Delegate::FunPtr fn) : delegate::detail::Delegate::Delegate(fn) {} - - template Delegate(F functional) : delegate::detail::Delegate::Delegate(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - delegate::detail::Delegate::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - delegate::detail::Delegate::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(typename delegate::detail::Delegate::FunPtr fn) { - delegate::detail::Delegate::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - delegate::detail::Delegate::operator=(nullptr); - return *this; - } -}; - -template class Delegate : public delegate::detail::Delegate -{ -public: - Delegate() : delegate::detail::Delegate::Delegate() {} - - Delegate(std::nullptr_t) : delegate::detail::Delegate::Delegate(nullptr) {} - - Delegate(const Delegate& del) : delegate::detail::Delegate::Delegate( - static_cast&>(del)) {} - - Delegate(Delegate&& del) : delegate::detail::Delegate::Delegate( - std::move(static_cast&>(del))) {} - - Delegate(typename delegate::detail::Delegate::FunPtr fn) : delegate::detail::Delegate::Delegate(fn) {} - - template Delegate(F functional) : delegate::detail::Delegate::Delegate(std::forward(functional)) {} - - Delegate& operator=(const Delegate& del) { - delegate::detail::Delegate::operator=(del); - return *this; - } - - Delegate& operator=(Delegate&& del) { - delegate::detail::Delegate::operator=(std::move(del)); - return *this; - } - - Delegate& operator=(typename delegate::detail::Delegate::FunPtr fn) { - delegate::detail::Delegate::operator=(fn); - return *this; - } - - inline Delegate& IRAM_ATTR operator=(std::nullptr_t) __attribute__((always_inline)) { - delegate::detail::Delegate::operator=(nullptr); - return *this; - } -}; - -#endif // __Delegate_h diff --git a/components/espsoftwareserial/circular_queue/MultiDelegate.h b/components/espsoftwareserial/circular_queue/MultiDelegate.h deleted file mode 100644 index aee0741..0000000 --- a/components/espsoftwareserial/circular_queue/MultiDelegate.h +++ /dev/null @@ -1,567 +0,0 @@ -/* -MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate -class -Copyright (c) 2019-2020 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __MULTIDELEGATE_H -#define __MULTIDELEGATE_H - -#include -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -#include -#else -#include "circular_queue/ghostl.h" -#endif - -#if defined(ESP8266) -#include -using esp8266::InterruptLock; -#elif defined(ARDUINO) -class InterruptLock { -public: - InterruptLock() { - noInterrupts(); - } - ~InterruptLock() { - interrupts(); - } -}; -#else -#include -#endif - -namespace -{ - - template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> - struct CallP - { - static R execute(Delegate& del, P... args) - { - return del(std::forward(args...)); - } - }; - - template< typename Delegate, bool ISQUEUE, typename... P> - struct CallP - { - static bool execute(Delegate& del, P... args) - { - del(std::forward(args...)); - return true; - } - }; - - template< typename Delegate, typename R, bool ISQUEUE = false> - struct Call - { - static R execute(Delegate& del) - { - return del(); - } - }; - - template< typename Delegate, bool ISQUEUE> - struct Call - { - static bool execute(Delegate& del) - { - del(); - return true; - } - }; - -} - -namespace delegate -{ - namespace detail - { - - template< typename Delegate, typename R, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P> - class MultiDelegatePImpl - { - public: - MultiDelegatePImpl() = default; - ~MultiDelegatePImpl() - { - *this = nullptr; - } - - MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; - MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; - - MultiDelegatePImpl(MultiDelegatePImpl&& md) - { - first = md.first; - last = md.last; - unused = md.unused; - nodeCount = md.nodeCount; - md.first = nullptr; - md.last = nullptr; - md.unused = nullptr; - md.nodeCount = 0; - } - - MultiDelegatePImpl(const Delegate& del) - { - add(del); - } - - MultiDelegatePImpl(Delegate&& del) - { - add(std::move(del)); - } - - MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) - { - first = md.first; - last = md.last; - unused = md.unused; - nodeCount = md.nodeCount; - md.first = nullptr; - md.last = nullptr; - md.unused = nullptr; - md.nodeCount = 0; - return *this; - } - - MultiDelegatePImpl& operator=(std::nullptr_t) - { - if (last) - last->mNext = unused; - if (first) - unused = first; - while (unused) - { - auto to_delete = unused; - unused = unused->mNext; - delete(to_delete); - } - return *this; - } - - MultiDelegatePImpl& operator+=(const Delegate& del) - { - add(del); - return *this; - } - - MultiDelegatePImpl& operator+=(Delegate&& del) - { - add(std::move(del)); - return *this; - } - - protected: - struct Node_t - { - ~Node_t() - { - mDelegate = nullptr; // special overload in Delegate - } - Node_t* mNext = nullptr; - Delegate mDelegate; - }; - - Node_t* first = nullptr; - Node_t* last = nullptr; - Node_t* unused = nullptr; - size_t nodeCount = 0; - - // Returns a pointer to an unused Node_t, - // or if none are available allocates a new one, - // or nullptr if limit is reached - Node_t* IRAM_ATTR get_node_unsafe() - { - Node_t* result = nullptr; - // try to get an item from unused items list - if (unused) - { - result = unused; - unused = unused->mNext; - } - // if no unused items, and count not too high, allocate a new one - else if (nodeCount < QUEUE_CAPACITY) - { -#if defined(ESP8266) || defined(ESP32) - result = new (std::nothrow) Node_t; -#else - result = new Node_t; -#endif - if (result) - ++nodeCount; - } - return result; - } - - void recycle_node_unsafe(Node_t* node) - { - node->mDelegate = nullptr; // special overload in Delegate - node->mNext = unused; - unused = node; - } - -#ifndef ARDUINO - std::mutex mutex_unused; -#endif - public: - class iterator : public std::iterator - { - public: - Node_t* current = nullptr; - Node_t* prev = nullptr; - const Node_t* stop = nullptr; - - iterator(MultiDelegatePImpl& md) : current(md.first), stop(md.last) {} - iterator() = default; - iterator(const iterator&) = default; - iterator& operator=(const iterator&) = default; - iterator& operator=(iterator&&) = default; - operator bool() const - { - return current && stop; - } - bool operator==(const iterator& rhs) const - { - return current == rhs.current; - } - bool operator!=(const iterator& rhs) const - { - return !operator==(rhs); - } - Delegate& operator*() const - { - return current->mDelegate; - } - Delegate* operator->() const - { - return ¤t->mDelegate; - } - iterator& operator++() // prefix - { - if (current && stop != current) - { - prev = current; - current = current->mNext; - } - else - current = nullptr; // end - return *this; - } - iterator& operator++(int) // postfix - { - iterator tmp(*this); - operator++(); - return tmp; - } - }; - - iterator begin() - { - return iterator(*this); - } - iterator end() const - { - return iterator(); - } - - const Delegate* add(const Delegate& del) - { - return add(Delegate(del)); - } - - const Delegate* add(Delegate&& del) - { - if (!del) - return nullptr; - -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - Node_t* item = ISQUEUE ? get_node_unsafe() : -#if defined(ESP8266) || defined(ESP32) - new (std::nothrow) Node_t; -#else - new Node_t; -#endif - if (!item) - return nullptr; - - item->mDelegate = std::move(del); - item->mNext = nullptr; - - if (last) - last->mNext = item; - else - first = item; - last = item; - - return &item->mDelegate; - } - - iterator erase(iterator it) - { - if (!it) - return end(); -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - auto to_recycle = it.current; - - if (last == it.current) - last = it.prev; - it.current = it.current->mNext; - if (it.prev) - { - it.prev->mNext = it.current; - } - else - { - first = it.current; - } - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - return it; - } - - bool erase(const Delegate* const del) - { - auto it = begin(); - while (it) - { - if (del == &(*it)) - { - erase(it); - return true; - } - ++it; - } - return false; - } - - operator bool() const - { - return first; - } - - R operator()(P... args) - { - auto it = begin(); - if (!it) - return {}; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return {}; - fence.store(true); -#else - if (fence.exchange(true)) return {}; -#endif - - R result; - do - { - result = CallP::execute(*it, args...); - if (result && ISQUEUE) - it = erase(it); - else - ++it; -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (it); - - fence.store(false); - return result; - } - }; - - template< typename Delegate, typename R = void, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32> - class MultiDelegateImpl : public MultiDelegatePImpl - { - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - - R operator()() - { - auto it = this->begin(); - if (!it) - return {}; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return {}; - fence.store(true); -#else - if (fence.exchange(true)) return {}; -#endif - - R result; - do - { - result = Call::execute(*it); - if (result && ISQUEUE) - it = this->erase(it); - else - ++it; -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (it); - - fence.store(false); - return result; - } - }; - - template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> class MultiDelegate; - - template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> - class MultiDelegate : public MultiDelegatePImpl - { - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - }; - - template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY> - class MultiDelegate : public MultiDelegateImpl - { - public: - using MultiDelegateImpl::MultiDelegateImpl; - }; - - template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> - class MultiDelegate : public MultiDelegatePImpl - { - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - - void operator()(P... args) - { - auto it = this->begin(); - if (!it) - return; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return; - fence.store(true); -#else - if (fence.exchange(true)) return; -#endif - - do - { - CallP::execute(*it, args...); - if (ISQUEUE) - it = this->erase(it); - else - ++it; -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (it); - - fence.store(false); - } - }; - - template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY> - class MultiDelegate : public MultiDelegateImpl - { - public: - using MultiDelegateImpl::MultiDelegateImpl; - - void operator()() - { - auto it = this->begin(); - if (!it) - return; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return; - fence.store(true); -#else - if (fence.exchange(true)) return; -#endif - - do - { - Call::execute(*it); - if (ISQUEUE) - it = this->erase(it); - else - ++it; -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (it); - - fence.store(false); - } - }; - - } - -} - -/** -The MultiDelegate class template can be specialized to either a queue or an event multiplexer. -It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. -@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. -@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), - the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. - This is exploited to minimize the use of new and delete by reusing already allocated items, thus - reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are - used for allocation of the event handler items. - If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue - removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until - explicitly removed. - If the result type of the function call operator of Delegate is non-void, in a MultiDelegate queue - the type-conversion to bool of that result determines if the item is immediately removed or kept - after each call: if true is returned, the item is removed. A Multidelegate event multiplexer keeps event - handlers until they are explicitly removed. -@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically - allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate - instance during its own lifetime for efficiency. -*/ -template< typename Delegate, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32> -class MultiDelegate : public delegate::detail::MultiDelegate -{ -public: - using delegate::detail::MultiDelegate::MultiDelegate; -}; - -#endif // __MULTIDELEGATE_H diff --git a/components/espsoftwareserial/circular_queue/circular_queue.h b/components/espsoftwareserial/circular_queue/circular_queue.h deleted file mode 100644 index 0d9a272..0000000 --- a/components/espsoftwareserial/circular_queue/circular_queue.h +++ /dev/null @@ -1,393 +0,0 @@ -/* -circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __circular_queue_h -#define __circular_queue_h - -#ifdef ARDUINO -#include -#endif - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -#include -#include -#include -#include "Delegate.h" -using std::min; -#else -#include "ghostl.h" -#endif - -#if !defined(ESP32) && !defined(ESP8266) -#define IRAM_ATTR -#endif - -/*! - @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). - This implementation is lock-free between producer and consumer for the available(), peek(), - pop(), and push() type functions. -*/ -template< typename T, typename ForEachArg = void > -class circular_queue -{ -public: - /*! - @brief Constructs a valid, but zero-capacity dummy queue. - */ - circular_queue() : m_bufSize(1) - { - m_inPos.store(0); - m_outPos.store(0); - } - /*! - @brief Constructs a queue of the given maximum capacity. - */ - circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) - { - m_inPos.store(0); - m_outPos.store(0); - } - circular_queue(circular_queue&& cq) : - m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) - {} - ~circular_queue() - { - m_buffer.reset(); - } - circular_queue(const circular_queue&) = delete; - circular_queue& operator=(circular_queue&& cq) - { - m_bufSize = cq.m_bufSize; - m_buffer = cq.m_buffer; - m_inPos.store(cq.m_inPos.load()); - m_outPos.store(cq.m_outPos.load()); - } - circular_queue& operator=(const circular_queue&) = delete; - - /*! - @brief Get the numer of elements the queue can hold at most. - */ - size_t capacity() const - { - return m_bufSize - 1; - } - - /*! - @brief Resize the queue. The available elements in the queue are preserved. - This is not lock-free and concurrent producer or consumer access - will lead to corruption. - @return True if the new capacity could accommodate the present elements in - the queue, otherwise nothing is done and false is returned. - */ - bool capacity(const size_t cap); - - /*! - @brief Discard all data in the queue. - */ - void flush() - { - m_outPos.store(m_inPos.load()); - } - - /*! - @brief Get a snapshot number of elements that can be retrieved by pop. - */ - size_t IRAM_ATTR available() const - { - int avail = static_cast(m_inPos.load() - m_outPos.load()); - if (avail < 0) avail += m_bufSize; - return avail; - } - - /*! - @brief Get the remaining free elementes for pushing. - */ - size_t IRAM_ATTR available_for_push() const - { - int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; - if (avail < 0) avail += m_bufSize; - return avail; - } - - /*! - @brief Peek at the next element pop will return without removing it from the queue. - @return An rvalue copy of the next element that can be popped. If the queue is empty, - return an rvalue copy of the element that is pending the next push. - */ - T peek() const - { - const auto outPos = m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - return m_buffer[outPos]; - } - - /*! - @brief Peek at the next pending input value. - @return A reference to the next element that can be pushed. - */ - T& IRAM_ATTR pushpeek() - { - const auto inPos = m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - return m_buffer[inPos]; - } - - /*! - @brief Release the next pending input value, accessible by pushpeek(), into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push() - { - const auto inPos = m_inPos.load(std::memory_order_acquire); - const size_t next = (inPos + 1) % m_bufSize; - if (next == m_outPos.load(std::memory_order_relaxed)) { - return false; - } - - std::atomic_thread_fence(std::memory_order_acquire); - - m_inPos.store(next, std::memory_order_release); - return true; - } - - /*! - @brief Move the rvalue parameter into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(T&& val) - { - const auto inPos = m_inPos.load(std::memory_order_acquire); - const size_t next = (inPos + 1) % m_bufSize; - if (next == m_outPos.load(std::memory_order_relaxed)) { - return false; - } - - std::atomic_thread_fence(std::memory_order_acquire); - - m_buffer[inPos] = std::move(val); - - std::atomic_thread_fence(std::memory_order_release); - - m_inPos.store(next, std::memory_order_release); - return true; - } - - /*! - @brief Push a copy of the parameter into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(const T& val) - { - T v(val); - return push(std::move(v)); - } - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - /*! - @brief Push copies of multiple elements from a buffer into the queue, - in order, beginning at buffer's head. - @return The number of elements actually copied into the queue, counted - from the buffer head. - */ - size_t push_n(const T* buffer, size_t size); -#endif - - /*! - @brief Pop the next available element from the queue. - @return An rvalue copy of the popped element, or a default - value of type T if the queue is empty. - */ - T pop(); - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - /*! - @brief Pop multiple elements in ordered sequence from the queue to a buffer. - If buffer is nullptr, simply discards up to size elements from the queue. - @return The number of elements actually popped from the queue to - buffer. - */ - size_t pop_n(T* buffer, size_t size); -#endif - - /*! - @brief Iterate over and remove each available element from queue, - calling back fun with an rvalue reference of every single element. - */ -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - void for_each(const Delegate& fun); -#else - void for_each(Delegate fun); -#endif - - /*! - @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, - calling back fun with a reference of every single element. - Requeuing is dependent on the return boolean of the callback function. If it - returns true, the requeue occurs. - */ -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - bool for_each_rev_requeue(const Delegate& fun); -#else - bool for_each_rev_requeue(Delegate fun); -#endif - -protected: - const T defaultValue {}; - size_t m_bufSize; -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - std::unique_ptr m_buffer; -#else - std::unique_ptr m_buffer; -#endif - std::atomic m_inPos; - std::atomic m_outPos; -}; - -template< typename T, typename ForEachArg > -bool circular_queue::capacity(const size_t cap) -{ - if (cap + 1 == m_bufSize) return true; - else if (available() > cap) return false; - std::unique_ptr buffer(new T[cap + 1]); - const auto available = pop_n(buffer, cap); - m_buffer.reset(buffer); - m_bufSize = cap + 1; - std::atomic_thread_fence(std::memory_order_release); - m_inPos.store(available, std::memory_order_relaxed); - m_outPos.store(0, std::memory_order_release); - return true; -} - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -template< typename T, typename ForEachArg > -size_t circular_queue::push_n(const T* buffer, size_t size) -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const auto outPos = m_outPos.load(std::memory_order_relaxed); - - size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; - blockSize = min(size, blockSize); - if (!blockSize) return 0; - int next = (inPos + blockSize) % m_bufSize; - - std::atomic_thread_fence(std::memory_order_acquire); - - auto dest = m_buffer.get() + inPos; - std::copy_n(std::make_move_iterator(buffer), blockSize, dest); - size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); - next += size; - dest = m_buffer.get(); - std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); - - std::atomic_thread_fence(std::memory_order_release); - - m_inPos.store(next, std::memory_order_release); - return blockSize + size; -} -#endif - -template< typename T, typename ForEachArg > -T circular_queue::pop() -{ - const auto outPos = m_outPos.load(std::memory_order_acquire); - if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue; - - std::atomic_thread_fence(std::memory_order_acquire); - - auto val = std::move(m_buffer[outPos]); - - std::atomic_thread_fence(std::memory_order_release); - - m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); - return val; -} - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -template< typename T, typename ForEachArg > -size_t circular_queue::pop_n(T* buffer, size_t size) { - size_t avail = size = min(size, available()); - if (!avail) return 0; - const auto outPos = m_outPos.load(std::memory_order_acquire); - size_t n = min(avail, static_cast(m_bufSize - outPos)); - - std::atomic_thread_fence(std::memory_order_acquire); - - if (buffer) { - buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); - avail -= n; - std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); - } - - std::atomic_thread_fence(std::memory_order_release); - - m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); - return size; -} -#endif - -template< typename T, typename ForEachArg > -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -void circular_queue::for_each(const Delegate& fun) -#else -void circular_queue::for_each(Delegate fun) -#endif -{ - auto outPos = m_outPos.load(std::memory_order_acquire); - const auto inPos = m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - while (outPos != inPos) - { - fun(std::move(m_buffer[outPos])); - std::atomic_thread_fence(std::memory_order_release); - outPos = (outPos + 1) % m_bufSize; - m_outPos.store(outPos, std::memory_order_release); - } -} - -template< typename T, typename ForEachArg > -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -bool circular_queue::for_each_rev_requeue(const Delegate& fun) -#else -bool circular_queue::for_each_rev_requeue(Delegate fun) -#endif -{ - auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); - auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (outPos == inPos0) return false; - auto pos = inPos0; - auto outPos1 = inPos0; - const auto posDecr = circular_queue::m_bufSize - 1; - do { - pos = (pos + posDecr) % circular_queue::m_bufSize; - T&& val = std::move(circular_queue::m_buffer[pos]); - if (fun(val)) - { - outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; - if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); - } - } while (pos != outPos); - circular_queue::m_outPos.store(outPos1, std::memory_order_release); - return true; -} - -#endif // __circular_queue_h diff --git a/components/espsoftwareserial/circular_queue/circular_queue_mp.h b/components/espsoftwareserial/circular_queue/circular_queue_mp.h deleted file mode 100644 index ba37689..0000000 --- a/components/espsoftwareserial/circular_queue/circular_queue_mp.h +++ /dev/null @@ -1,200 +0,0 @@ -/* -circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __circular_queue_mp_h -#define __circular_queue_mp_h - -#include "circular_queue.h" - -#ifdef ESP8266 -#include "interrupts.h" -#else -#include -#endif - -/*! - @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). - This implementation is lock-free between producers and consumer for the available(), peek(), - pop(), and push() type functions, but is guarded to safely allow only a single producer - at any instant. -*/ -template< typename T, typename ForEachArg = void > -class circular_queue_mp : protected circular_queue -{ -public: - circular_queue_mp() = default; - circular_queue_mp(const size_t capacity) : circular_queue(capacity) - {} - circular_queue_mp(circular_queue&& cq) : circular_queue(std::move(cq)) - {} - using circular_queue::operator=; - using circular_queue::capacity; - using circular_queue::flush; - using circular_queue::available; - using circular_queue::available_for_push; - using circular_queue::peek; - using circular_queue::pop; - using circular_queue::pop_n; - using circular_queue::for_each; - using circular_queue::for_each_rev_requeue; - - /*! - @brief Resize the queue. The available elements in the queue are preserved. - This is not lock-free, but safe, concurrent producer or consumer access - is guarded. - @return True if the new capacity could accommodate the present elements in - the queue, otherwise nothing is done and false is returned. - */ - bool capacity(const size_t cap) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::capacity(cap); - } - - bool IRAM_ATTR push() = delete; - - /*! - @brief Move the rvalue parameter into the queue, guarded - for multiple concurrent producers. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(T&& val) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push(std::move(val)); - } - - /*! - @brief Push a copy of the parameter into the queue, guarded - for multiple concurrent producers. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(const T& val) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push(val); - } - - /*! - @brief Push copies of multiple elements from a buffer into the queue, - in order, beginning at buffer's head. This is guarded for - multiple producers, push_n() is atomic. - @return The number of elements actually copied into the queue, counted - from the buffer head. - */ - size_t push_n(const T* buffer, size_t size) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push_n(buffer, size); - } - - /*! - @brief Pops the next available element from the queue, requeues - it immediately. - @return A reference to the just requeued element, or the default - value of type T if the queue is empty. - */ - T& pop_requeue(); - - /*! - @brief Iterate over, pop and optionally requeue each available element from the queue, - calling back fun with a reference of every single element. - Requeuing is dependent on the return boolean of the callback function. If it - returns true, the requeue occurs. - */ - bool for_each_requeue(const Delegate& fun); - -#ifndef ESP8266 -protected: - std::mutex m_pushMtx; -#endif -}; - -template< typename T, typename ForEachArg > -T& circular_queue_mp::pop_requeue() -{ -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - const auto outPos = circular_queue::m_outPos.load(std::memory_order_acquire); - const auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (inPos == outPos) return circular_queue::defaultValue; - T& val = circular_queue::m_buffer[inPos] = std::move(circular_queue::m_buffer[outPos]); - const auto bufSize = circular_queue::m_bufSize; - std::atomic_thread_fence(std::memory_order_release); - circular_queue::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed); - circular_queue::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release); - return val; -} - -template< typename T, typename ForEachArg > -bool circular_queue_mp::for_each_requeue(const Delegate& fun) -{ - auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); - auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (outPos == inPos0) return false; - do { - T&& val = std::move(circular_queue::m_buffer[outPos]); - if (fun(val)) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - std::atomic_thread_fence(std::memory_order_release); - auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - circular_queue::m_buffer[inPos] = std::move(val); - std::atomic_thread_fence(std::memory_order_release); - circular_queue::m_inPos.store((inPos + 1) % circular_queue::m_bufSize, std::memory_order_release); - } - else - { - std::atomic_thread_fence(std::memory_order_release); - } - outPos = (outPos + 1) % circular_queue::m_bufSize; - circular_queue::m_outPos.store(outPos, std::memory_order_release); - } while (outPos != inPos0); - return true; -} - -#endif // __circular_queue_mp_h diff --git a/components/espsoftwareserial/circular_queue/ghostl.h b/components/espsoftwareserial/circular_queue/ghostl.h deleted file mode 100644 index ba3f863..0000000 --- a/components/espsoftwareserial/circular_queue/ghostl.h +++ /dev/null @@ -1,94 +0,0 @@ -/* -ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell - that allows building some Arduino ESP8266/ESP32 - libraries on Aruduino AVR. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __ghostl_h -#define __ghostl_h - -#if defined(ARDUINO_ARCH_SAMD) -#include -#endif - -using size_t = decltype(sizeof(char)); - -namespace std -{ -#if !defined(ARDUINO_ARCH_SAMD) - typedef enum memory_order { - memory_order_relaxed, - memory_order_acquire, - memory_order_release, - memory_order_seq_cst - } memory_order; - template< typename T > class atomic { - private: - T value; - public: - atomic() {} - atomic(T desired) { value = desired; } - void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } - T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } - }; - inline void atomic_thread_fence(std::memory_order order) noexcept {} - template< typename T > T&& move(T& t) noexcept { return static_cast(t); } -#endif - - template< typename T, size_t long N > struct array - { - T _M_elems[N]; - decltype(sizeof(0)) size() const { return N; } - T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; } - const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; } - }; - - template< typename T > class unique_ptr - { - public: - using pointer = T*; - unique_ptr() noexcept : ptr(nullptr) {} - unique_ptr(pointer p) : ptr(p) {} - pointer operator->() const noexcept { return ptr; } - T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; } - void reset(pointer p = pointer()) noexcept - { - delete ptr; - ptr = p; - } - T& operator*() const { return *ptr; } - private: - pointer ptr; - }; - - template< typename T > using function = T*; - using nullptr_t = decltype(nullptr); - - template - struct identity { - typedef T type; - }; - - template - inline T&& forward(typename identity::type& t) noexcept - { - return static_cast::type&&>(t); - } -} - -#endif // __ghostl_h