├── .gitignore ├── extras ├── test │ ├── cores │ │ ├── dxcore │ │ │ ├── EEPROM.cpp │ │ │ ├── EEPROM.h │ │ │ └── CMakeLists.txt │ │ ├── rp2040 │ │ │ ├── EEPROM.h │ │ │ ├── EEPROM.cpp │ │ │ └── CMakeLists.txt │ │ ├── stm32 │ │ │ ├── EEPROM.h │ │ │ ├── EEPROM.cpp │ │ │ ├── CMakeLists.txt │ │ │ └── Stream.h │ │ ├── esp32 │ │ │ ├── EEPROM.h │ │ │ ├── EEPROM.cpp │ │ │ ├── CMakeLists.txt │ │ │ └── Stream.h │ │ ├── esp8266 │ │ │ ├── EEPROM.h │ │ │ ├── EEPROM.cpp │ │ │ ├── CMakeLists.txt │ │ │ └── Stream.h │ │ ├── coreapi │ │ │ ├── Arduino.h │ │ │ ├── Common.h │ │ │ ├── Client.h │ │ │ ├── Stream.h │ │ │ ├── Print.h │ │ │ └── WString.h │ │ ├── avr │ │ │ ├── EEPROM.cpp │ │ │ ├── EEPROM.h │ │ │ ├── avr │ │ │ │ └── pgmspace.h │ │ │ ├── Arduino.h │ │ │ ├── CMakeLists.txt │ │ │ ├── Client.h │ │ │ ├── Stream.h │ │ │ ├── Print.h │ │ │ └── WString.h │ │ ├── teensy │ │ │ ├── EEPROM.cpp │ │ │ ├── EEPROM.h │ │ │ └── CMakeLists.txt │ │ ├── samd │ │ │ └── CMakeLists.txt │ │ ├── nrf52 │ │ │ ├── CMakeLists.txt │ │ │ ├── Stream.h │ │ │ └── Print.h │ │ ├── stm32f1 │ │ │ ├── CMakeLists.txt │ │ │ ├── Client.h │ │ │ ├── Stream.h │ │ │ └── Print.h │ │ ├── mbed │ │ │ └── CMakeLists.txt │ │ └── stm32f4 │ │ │ └── CMakeLists.txt │ ├── doctest │ │ ├── CMakeLists.txt │ │ └── main.cpp │ ├── FailingAllocator.hpp │ ├── HammingClientTest.cpp │ ├── HammingStreamTest.cpp │ ├── HammingEncodingClientTest.cpp │ ├── HammingEncodingStreamTest.cpp │ ├── SpyingAllocator.hpp │ ├── CMakeLists.txt │ ├── LoggingPrintTest.cpp │ ├── EepromStreamTest.cpp │ ├── StringPrintTest.cpp │ ├── ReadLoggingStreamTest.cpp │ ├── ReadThrottlingStreamTest.cpp │ ├── WriteLoggingStreamTest.cpp │ ├── LoggingStreamTest.cpp │ ├── ChunkDecodingClientTest.cpp │ ├── ProgmemStreamTest.cpp │ ├── WaitingPrintTest.cpp │ ├── WriteWaitingClientTest.cpp │ ├── WriteWaitingStreamTest.cpp │ ├── MemoryStreamTest.cpp │ └── StringStreamTest.cpp └── images │ ├── StringPrint.svg │ ├── ProgmemStream.svg │ ├── EepromStream.svg │ ├── StringStream.svg │ ├── HammingDecodingStream.svg │ ├── ReadBuffer.svg │ ├── WriteBuffer.svg │ ├── ChunkDecodingStream.svg │ ├── HammingEncodingStream.svg │ ├── ReadLogger.svg │ ├── WriteLogger.svg │ ├── HammingStream.svg │ ├── Logger.svg │ └── WriteWaitingStream.svg ├── .github ├── FUNDING.yml └── workflows │ └── lock.yml ├── .clang-format ├── src ├── StreamUtils.h ├── StreamUtils │ ├── Ports │ │ ├── DefaultAllocator.hpp │ │ └── ArduinoThrottler.hpp │ ├── Prints │ │ ├── LoggingPrint.hpp │ │ ├── SpyingPrint.hpp │ │ ├── HammingPrint.hpp │ │ ├── WaitingPrint.hpp │ │ ├── BufferingPrint.hpp │ │ ├── StringPrint.hpp │ │ └── PrintProxy.hpp │ ├── Helpers.hpp │ ├── Policies │ │ ├── WriteForwardingPolicy.hpp │ │ ├── ReadForwardingPolicy.hpp │ │ ├── ConnectForwardingPolicy.hpp │ │ ├── WriteLoggingPolicy.hpp │ │ ├── ReadLoggingPolicy.hpp │ │ ├── ReadThrottlingPolicy.hpp │ │ ├── WriteSpyingPolicy.hpp │ │ ├── WriteWaitingPolicy.hpp │ │ ├── ConnectSpyingPolicy.hpp │ │ ├── ReadSpyingPolicy.hpp │ │ ├── WriteBufferingPolicy.hpp │ │ ├── ReadBufferingPolicy.hpp │ │ └── HammingEncodingPolicy.hpp │ ├── Streams │ │ ├── SpyingStream.hpp │ │ ├── LoggingStream.hpp │ │ ├── ReadLoggingStream.hpp │ │ ├── WriteLoggingStream.hpp │ │ ├── HammingEncodingStream.hpp │ │ ├── HammingDecodingStream.hpp │ │ ├── HammingStream.hpp │ │ ├── ChunkDecodingStream.hpp │ │ ├── WriteWaitingStream.hpp │ │ ├── WriteBufferingStream.hpp │ │ ├── ReadBufferingStream.hpp │ │ ├── ReadThrottlingStream.hpp │ │ ├── ProgmemStream.hpp │ │ ├── MemoryStream.hpp │ │ ├── StreamProxy.hpp │ │ ├── EepromStream.hpp │ │ └── StringStream.hpp │ ├── Clients │ │ ├── SpyingClient.hpp │ │ ├── HammingDecodingClient.hpp │ │ ├── HammingEncodingClient.hpp │ │ ├── HammingClient.hpp │ │ ├── LoggingClient.hpp │ │ ├── WriteLoggingClient.hpp │ │ ├── ChunkDecodingClient.hpp │ │ ├── ReadLoggingClient.hpp │ │ ├── WriteWaitingClient.hpp │ │ ├── WriteBufferingClient.hpp │ │ ├── ReadBufferingClient.hpp │ │ ├── MemoryClient.hpp │ │ └── ClientProxy.hpp │ ├── Buffers │ │ ├── CharArray.hpp │ │ ├── CircularBuffer.hpp │ │ └── LinearBuffer.hpp │ ├── Polyfills.hpp │ └── Configuration.hpp └── StreamUtils.hpp ├── keywords.txt ├── CMakeLists.txt ├── library.properties ├── LICENSE.md ├── examples ├── ChunkDecoding │ └── ChunkDecoding.ino ├── StringPrint │ └── StringPrint.ino ├── ProgmemStream │ └── ProgmemStream.ino ├── StringStream │ └── StringStream.ino ├── WriteLogger │ └── WriteLogger.ino ├── ReadLogger │ └── ReadLogger.ino ├── EepromRead │ └── EepromRead.ino ├── ReadBuffer │ └── ReadBuffer.ino ├── Logger │ └── Logger.ino ├── EepromWrite │ └── EepromWrite.ino ├── HammingSoftwareSerial │ └── HammingSoftwareSerial.ino ├── HammingSerial1 │ └── HammingSerial1.ino └── WriteBuffer │ └── WriteBuffer.ino └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.vs/ 3 | /out/ -------------------------------------------------------------------------------- /extras/test/cores/dxcore/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "../avr/EEPROM.cpp" -------------------------------------------------------------------------------- /extras/test/cores/dxcore/EEPROM.h: -------------------------------------------------------------------------------- 1 | #include "../avr/EEPROM.h" -------------------------------------------------------------------------------- /extras/test/cores/rp2040/EEPROM.h: -------------------------------------------------------------------------------- 1 | #include "../esp32/EEPROM.h" -------------------------------------------------------------------------------- /extras/test/cores/rp2040/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "../esp32/EEPROM.cpp" -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bblanchon 2 | custom: 3 | - https://arduinojson.org/book/ 4 | - https://donate.benoitblanchon.fr/ 5 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AllowShortFunctionsOnASingleLine: Empty 3 | AllowShortIfStatementsOnASingleLine: false 4 | AllowShortLoopsOnASingleLine: false 5 | IncludeBlocks: Preserve -------------------------------------------------------------------------------- /extras/test/doctest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(doctest 2 | doctest.h 3 | main.cpp 4 | ) 5 | 6 | target_include_directories(doctest 7 | PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR} 9 | ) -------------------------------------------------------------------------------- /src/StreamUtils.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils.hpp" 6 | 7 | using namespace StreamUtils; -------------------------------------------------------------------------------- /extras/test/doctest/main.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 6 | #include "doctest.h" 7 | -------------------------------------------------------------------------------- /extras/test/cores/stm32/EEPROM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class EEPROMClass { 6 | public: 7 | uint8_t read(int); 8 | void write(int, uint8_t); 9 | }; 10 | 11 | extern EEPROMClass EEPROM; 12 | -------------------------------------------------------------------------------- /extras/test/cores/esp32/EEPROM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class EEPROMClass { 6 | public: 7 | uint8_t read(int); 8 | void write(int, uint8_t); 9 | bool commit(); 10 | }; 11 | 12 | extern EEPROMClass EEPROM; 13 | -------------------------------------------------------------------------------- /extras/test/cores/esp8266/EEPROM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class EEPROMClass { 6 | public: 7 | uint8_t read(int); 8 | void write(int, uint8_t); 9 | void commit(); 10 | }; 11 | 12 | extern EEPROMClass EEPROM; 13 | -------------------------------------------------------------------------------- /extras/test/cores/coreapi/Arduino.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Common.h" 8 | #include "Print.h" 9 | #include "Stream.h" 10 | #include "WString.h" -------------------------------------------------------------------------------- /extras/test/cores/avr/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "EEPROM.h" 2 | 3 | EEPROMClass EEPROM; 4 | static uint8_t data[512]; 5 | 6 | uint8_t EEPROMClass::read(int address) { 7 | return data[address]; 8 | } 9 | 10 | void EEPROMClass::update(int address, uint8_t value) { 11 | data[address] = value; 12 | } 13 | -------------------------------------------------------------------------------- /extras/test/cores/stm32/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "EEPROM.h" 2 | 3 | EEPROMClass EEPROM; 4 | static uint8_t data[512]; 5 | 6 | uint8_t EEPROMClass::read(int address) { 7 | return data[address]; 8 | } 9 | 10 | void EEPROMClass::write(int address, uint8_t value) { 11 | data[address] = value; 12 | } 13 | -------------------------------------------------------------------------------- /extras/test/cores/avr/EEPROM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class EEPROMClass { 6 | public: 7 | uint8_t read(int); 8 | void update(int, uint8_t); 9 | // void write(int, uint8_t); <- it exists but we want to use update() instead 10 | }; 11 | 12 | extern EEPROMClass EEPROM; 13 | -------------------------------------------------------------------------------- /extras/test/cores/teensy/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "EEPROM.h" 2 | 3 | EEPROMClass EEPROM; 4 | static uint8_t data[512]; 5 | 6 | uint8_t EEPROMClass::read(int address) { 7 | return data[address]; 8 | } 9 | 10 | void EEPROMClass::update(int address, uint8_t value) { 11 | data[address] = value; 12 | } 13 | -------------------------------------------------------------------------------- /extras/test/cores/teensy/EEPROM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class EEPROMClass { 6 | public: 7 | uint8_t read(int); 8 | void update(int, uint8_t); 9 | // void write(int, uint8_t); <- it exists but we want to use update() instead 10 | }; 11 | 12 | extern EEPROMClass EEPROM; 13 | -------------------------------------------------------------------------------- /extras/test/cores/coreapi/Common.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | inline unsigned long millis() { 10 | return static_cast(time(NULL)); 11 | } 12 | 13 | inline void yield() {} -------------------------------------------------------------------------------- /extras/test/FailingAllocator.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include // size_t 8 | 9 | struct FailingAllocator { 10 | void* allocate(size_t) { 11 | return nullptr; 12 | } 13 | void deallocate(void*) {} 14 | }; 15 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 1 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | lock: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: dessant/lock-threads@v5 17 | with: 18 | github-token: ${{ github.token }} 19 | issue-inactive-days: 30 20 | -------------------------------------------------------------------------------- /extras/test/cores/avr/avr/pgmspace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | inline size_t strlen_P(const char* s) { 7 | return strlen(s - 42); 8 | } 9 | 10 | inline uint8_t pgm_read_byte(const char* p) { 11 | return static_cast(p[-42]); 12 | } 13 | 14 | inline void* memcpy_P(void* dest, const void* src, size_t size) { 15 | return memcpy(dest, reinterpret_cast(src) - 42, size); 16 | } -------------------------------------------------------------------------------- /extras/test/cores/avr/Arduino.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | inline unsigned long millis() { 16 | return static_cast(time(NULL)); 17 | } 18 | 19 | inline void yield() {} -------------------------------------------------------------------------------- /src/StreamUtils/Ports/DefaultAllocator.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | namespace StreamUtils { 8 | 9 | #include // malloc, free, size_t 10 | 11 | struct DefaultAllocator { 12 | void* allocate(size_t n) { 13 | return malloc(n); 14 | } 15 | 16 | void deallocate(void* p) { 17 | free(p); 18 | } 19 | }; 20 | 21 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/esp8266/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "EEPROM.h" 2 | 3 | #include // memcpy 4 | 5 | EEPROMClass EEPROM; 6 | static uint8_t commitedData[512]; 7 | static uint8_t pendingData[512]; 8 | 9 | uint8_t EEPROMClass::read(int address) { 10 | return commitedData[address]; 11 | } 12 | 13 | void EEPROMClass::write(int address, uint8_t value) { 14 | pendingData[address] = value; 15 | } 16 | 17 | void EEPROMClass::commit() { 18 | memcpy(commitedData, pendingData, 512); 19 | } -------------------------------------------------------------------------------- /extras/test/cores/samd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(SamdCore INTERFACE) 6 | 7 | target_include_directories(SamdCore 8 | INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 11 | ) 12 | 13 | target_compile_definitions(SamdCore 14 | INTERFACE 15 | ARDUINO_ARCH_SAMD 16 | ) 17 | 18 | add_streamutils_test(StreamUtilsTestSamd SamdCore) 19 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | BufferingPrint KEYWORD1 2 | EepromStream KEYWORD1 3 | LoggingClient KEYWORD1 4 | LoggingPrint KEYWORD1 5 | LoggingStream KEYWORD1 6 | ProgmemStream KEYWORD1 7 | ReadBufferingClient KEYWORD1 8 | ReadBufferingStream KEYWORD1 9 | ReadLoggingClient KEYWORD1 10 | ReadLoggingStream KEYWORD1 11 | ReadThrottlingStream KEYWORD1 12 | StringStream KEYWORD1 13 | WriteBufferingClient KEYWORD1 14 | WriteBufferingStream KEYWORD1 15 | WriteLoggingClient KEYWORD1 16 | WriteLoggingStream KEYWORD1 -------------------------------------------------------------------------------- /extras/test/cores/nrf52/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Nrf52Core INTERFACE) 6 | 7 | target_include_directories(Nrf52Core 8 | INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 11 | ) 12 | 13 | target_compile_definitions(Nrf52Core 14 | INTERFACE 15 | ARDUINO_ARCH_NRF52 16 | ) 17 | 18 | add_streamutils_test(StreamUtilsTestNrf52 Nrf52Core) 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | cmake_minimum_required(VERSION 3.12) 6 | project(StreamUtils) 7 | 8 | enable_testing() 9 | 10 | set(CMAKE_CXX_STANDARD 11) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | 13 | if(${COVERAGE}) 14 | set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage -g -O0") 15 | endif() 16 | 17 | include_directories(${CMAKE_CURRENT_LIST_DIR}/src) 18 | add_subdirectory(extras/test) 19 | -------------------------------------------------------------------------------- /extras/test/cores/esp32/EEPROM.cpp: -------------------------------------------------------------------------------- 1 | #include "EEPROM.h" 2 | 3 | #include // memcpy 4 | 5 | EEPROMClass EEPROM; 6 | static uint8_t commitedData[512]; 7 | static uint8_t pendingData[512]; 8 | 9 | uint8_t EEPROMClass::read(int address) { 10 | return commitedData[address]; 11 | } 12 | 13 | void EEPROMClass::write(int address, uint8_t value) { 14 | pendingData[address] = value; 15 | } 16 | 17 | bool EEPROMClass::commit() { 18 | memcpy(commitedData, pendingData, 512); 19 | return true; 20 | } -------------------------------------------------------------------------------- /extras/test/cores/avr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(AvrCore 6 | Client.h 7 | EEPROM.cpp 8 | EEPROM.h 9 | Print.h 10 | Stream.h 11 | ) 12 | 13 | target_include_directories(AvrCore 14 | PUBLIC 15 | ${CMAKE_CURRENT_SOURCE_DIR} 16 | ) 17 | 18 | target_compile_definitions(AvrCore 19 | PUBLIC 20 | ARDUINO_ARCH_AVR 21 | ) 22 | 23 | add_streamutils_test(StreamUtilsTestAvr AvrCore) 24 | -------------------------------------------------------------------------------- /extras/test/cores/teensy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(TeensyCore 6 | EEPROM.cpp 7 | EEPROM.h 8 | ) 9 | 10 | target_include_directories(TeensyCore 11 | PUBLIC 12 | ${CMAKE_CURRENT_SOURCE_DIR} 13 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 14 | ) 15 | 16 | target_compile_definitions(TeensyCore 17 | PUBLIC 18 | CORE_TEENSY 19 | ) 20 | 21 | add_streamutils_test(StreamUtilsTestTeensy TeensyCore) -------------------------------------------------------------------------------- /src/StreamUtils/Prints/LoggingPrint.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/WriteLoggingPolicy.hpp" 8 | #include "PrintProxy.hpp" 9 | 10 | namespace StreamUtils { 11 | 12 | struct LoggingPrint : PrintProxy { 13 | LoggingPrint(Print &upstream, Print &log) 14 | : PrintProxy(upstream, {log}) {} 15 | }; 16 | 17 | } // namespace StreamUtils -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=StreamUtils 2 | version=1.9.1 3 | author=Benoit Blanchon 4 | maintainer=Benoit Blanchon 5 | sentence=💪 Power-ups for Arduino streams 6 | paragraph=Enhances existing streams with logging, buffering, error correction, and more! Works with Serial, SoftwareSerial, WiFiClient... 7 | category=Other 8 | url=https://github.com/bblanchon/ArduinoStreamUtils 9 | architectures=* 10 | repository=https://github.com/bblanchon/ArduinoStreamUtils.git 11 | license=MIT 12 | -------------------------------------------------------------------------------- /src/StreamUtils/Helpers.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | namespace StreamUtils { 8 | 9 | inline size_t readOrReadBytes(Stream &stream, char *buffer, size_t size) { 10 | return stream.readBytes(buffer, size); 11 | } 12 | 13 | inline size_t readOrReadBytes(Client &client, char *buffer, size_t size) { 14 | return client.read(reinterpret_cast(buffer), size); 15 | } 16 | 17 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Prints/SpyingPrint.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/WriteSpyingPolicy.hpp" 8 | #include "PrintProxy.hpp" 9 | 10 | namespace StreamUtils { 11 | 12 | struct SpyingPrint : PrintProxy { 13 | SpyingPrint(Print &target, Print &log) 14 | : PrintProxy(target, WriteSpyingPolicy{log}) {} 15 | }; 16 | 17 | } // namespace StreamUtils 18 | -------------------------------------------------------------------------------- /extras/test/cores/esp8266/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Esp8266Core 6 | EEPROM.cpp 7 | EEPROM.h 8 | Stream.h 9 | ) 10 | 11 | target_include_directories(Esp8266Core 12 | PUBLIC 13 | ${CMAKE_CURRENT_SOURCE_DIR} 14 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 15 | ) 16 | 17 | target_compile_definitions(Esp8266Core 18 | PUBLIC 19 | ARDUINO_ARCH_ESP8266 20 | ) 21 | 22 | add_streamutils_test(StreamUtilsTestEsp8266 Esp8266Core) 23 | -------------------------------------------------------------------------------- /extras/test/cores/rp2040/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Rp2040Core 6 | EEPROM.cpp 7 | EEPROM.h 8 | ) 9 | 10 | target_include_directories(Rp2040Core 11 | PUBLIC 12 | ${CMAKE_CURRENT_SOURCE_DIR} 13 | ${CMAKE_CURRENT_SOURCE_DIR}/../coreapi 14 | ) 15 | 16 | target_compile_definitions(Rp2040Core 17 | PUBLIC 18 | ARDUINO_ARCH_RP2040 19 | ARDUINO_PICO_MAJOR=3 20 | ) 21 | 22 | add_streamutils_test(StreamUtilsTestRp2040 Rp2040Core) 23 | -------------------------------------------------------------------------------- /extras/test/cores/stm32f1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Stm32F1Core INTERFACE) 6 | 7 | target_include_directories(Stm32F1Core 8 | INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 11 | ) 12 | 13 | target_compile_definitions(Stm32F1Core 14 | INTERFACE 15 | ARDUINO_ARCH_STM32F1 16 | ) 17 | 18 | target_compile_options(Stm32F1Core 19 | INTERFACE 20 | -Wno-overloaded-virtual 21 | ) 22 | 23 | add_streamutils_test(StreamUtilsTestStm32F1 Stm32F1Core) 24 | -------------------------------------------------------------------------------- /extras/test/cores/mbed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(MbedRp2040Core INTERFACE) 6 | 7 | target_include_directories(MbedRp2040Core 8 | INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../coreapi 11 | ) 12 | 13 | target_compile_definitions(MbedRp2040Core 14 | INTERFACE 15 | ARDUINO_ARCH_RP2040 16 | ARDUINO_RASPBERRY_PI_PICO 17 | ARDUINO_ARCH_MBED_RP2040 18 | ARDUINO_ARCH_MBED 19 | ) 20 | 21 | add_streamutils_test(StreamUtilsTestMbedRp2040 MbedRp2040Core) 22 | -------------------------------------------------------------------------------- /src/StreamUtils/Prints/HammingPrint.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/HammingEncodingPolicy.hpp" 8 | #include "../Ports/DefaultAllocator.hpp" 9 | #include "PrintProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | template 14 | using BasicHammingPrint = PrintProxy>; 15 | 16 | template 17 | using HammingPrint = BasicHammingPrint; 18 | 19 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/stm32/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Stm32Core 6 | EEPROM.cpp 7 | EEPROM.h 8 | Stream.h 9 | ) 10 | 11 | target_include_directories(Stm32Core 12 | PUBLIC 13 | ${CMAKE_CURRENT_SOURCE_DIR} 14 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 15 | ) 16 | 17 | target_compile_definitions(Stm32Core 18 | PUBLIC 19 | ARDUINO_ARCH_STM32 20 | STM32_CORE_VERSION_MAJOR=2 21 | STM32_CORE_VERSION_MINOR=4 22 | STM32_CORE_VERSION_PATCH=0 23 | ) 24 | 25 | add_streamutils_test(StreamUtilsTestStm32 Stm32Core) 26 | -------------------------------------------------------------------------------- /extras/test/cores/avr/Client.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | using IPAddress = String; 11 | 12 | class Client : public Stream { 13 | public: 14 | virtual int connect(IPAddress ip, uint16_t port) = 0; 15 | virtual int connect(const char *host, uint16_t port) = 0; 16 | virtual uint8_t connected() = 0; 17 | virtual void stop() = 0; 18 | virtual operator bool() = 0; 19 | 20 | virtual int read() = 0; 21 | virtual int read(uint8_t *buf, size_t size) = 0; 22 | }; 23 | -------------------------------------------------------------------------------- /extras/test/cores/dxcore/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(DxCore 6 | EEPROM.cpp 7 | EEPROM.h 8 | ) 9 | 10 | target_include_directories(DxCore 11 | INTERFACE 12 | ${CMAKE_CURRENT_SOURCE_DIR} 13 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 14 | ) 15 | 16 | target_compile_definitions(DxCore 17 | PUBLIC 18 | ARDUINO_avrda 19 | ARDUINO_ARCH_MEGAAVR 20 | DXCORE=\"$\" 21 | DXCORE_MAJOR=1UL 22 | DXCORE_MINOR=5UL 23 | DXCORE_PATCH=11UL 24 | DXCORE_RELEASED=1 25 | ) 26 | 27 | add_streamutils_test(StreamUtilsTestDxCore DxCore) 28 | -------------------------------------------------------------------------------- /extras/test/cores/stm32f4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Stm32F4Core INTERFACE) 6 | 7 | target_include_directories(Stm32F4Core 8 | INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../stm32f1 11 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 12 | ) 13 | 14 | target_compile_definitions(Stm32F4Core 15 | INTERFACE 16 | ARDUINO_ARCH_STM32F4 17 | ) 18 | 19 | target_compile_options(Stm32F4Core 20 | INTERFACE 21 | -Wno-overloaded-virtual 22 | ) 23 | 24 | add_streamutils_test(StreamUtilsTestStm32F4 Stm32F4Core) 25 | -------------------------------------------------------------------------------- /src/StreamUtils/Policies/WriteForwardingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "../Configuration.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct WriteForwardingPolicy { 15 | template 16 | size_t write(Stream &stream, Args... args) { 17 | return stream.write(args...); 18 | } 19 | 20 | void flush(Stream &stream) { 21 | stream.flush(); 22 | } 23 | 24 | void implicitFlush(Stream &) {} 25 | }; 26 | 27 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Streams/SpyingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadSpyingPolicy.hpp" 8 | #include "../Policies/WriteSpyingPolicy.hpp" 9 | #include "StreamProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | struct SpyingStream : StreamProxy { 14 | SpyingStream(Stream &target, Print &log) 15 | : StreamProxy( 16 | target, ReadSpyingPolicy{log}, WriteSpyingPolicy{log}) {} 17 | }; 18 | 19 | } // namespace StreamUtils 20 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/LoggingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadLoggingPolicy.hpp" 8 | #include "../Policies/WriteLoggingPolicy.hpp" 9 | #include "StreamProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | struct LoggingStream : StreamProxy { 14 | LoggingStream(Stream& target, Print& log) 15 | : StreamProxy( 16 | target, ReadLoggingPolicy{log}, WriteLoggingPolicy{log}) {} 17 | }; 18 | 19 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Prints/WaitingPrint.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/WriteWaitingPolicy.hpp" 8 | #include "PrintProxy.hpp" 9 | 10 | namespace StreamUtils { 11 | 12 | struct WaitingPrint : PrintProxy { 13 | WaitingPrint(Print &target, Polyfills::function wait = yield) 14 | : PrintProxy( 15 | target, WriteWaitingPolicy{Polyfills::move(wait)}) {} 16 | 17 | void setTimeout(unsigned long timeout) { 18 | _writer.setTimeout(timeout); 19 | } 20 | }; 21 | 22 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Streams/ReadLoggingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadLoggingPolicy.hpp" 8 | #include "../Policies/WriteForwardingPolicy.hpp" 9 | #include "StreamProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | struct ReadLoggingStream 14 | : StreamProxy { 15 | ReadLoggingStream(Stream &target, Print &log) 16 | : StreamProxy( 17 | target, ReadLoggingPolicy{log}, WriteForwardingPolicy{}) {} 18 | }; 19 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Streams/WriteLoggingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadForwardingPolicy.hpp" 8 | #include "../Policies/WriteLoggingPolicy.hpp" 9 | #include "StreamProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | struct WriteLoggingStream 14 | : StreamProxy { 15 | WriteLoggingStream(Stream &target, Print &log) 16 | : StreamProxy( 17 | target, ReadForwardingPolicy{}, WriteLoggingPolicy{log}) {} 18 | }; 19 | 20 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Ports/ArduinoThrottler.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | namespace StreamUtils { 8 | 9 | class ArduinoThrottler { 10 | public: 11 | ArduinoThrottler(uint32_t rate) : _interval(1000000 / rate), _last(0) {} 12 | 13 | void throttle() { 14 | auto now = micros(); 15 | auto elapsed = now - _last; 16 | 17 | if (elapsed < _interval) { 18 | delayMicroseconds(_interval - elapsed); 19 | } 20 | 21 | _last = now; 22 | } 23 | 24 | private: 25 | unsigned long _interval; 26 | unsigned long _last; 27 | }; 28 | 29 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/coreapi/Client.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Stream.h" 8 | #include "WString.h" 9 | 10 | namespace arduino { 11 | 12 | using IPAddress = String; 13 | 14 | class Client : public Stream { 15 | public: 16 | virtual int connect(IPAddress ip, uint16_t port) = 0; 17 | virtual int connect(const char *host, uint16_t port) = 0; 18 | virtual uint8_t connected() = 0; 19 | virtual void stop() = 0; 20 | virtual operator bool() = 0; 21 | 22 | virtual int read() = 0; 23 | virtual int read(uint8_t *buf, size_t size) = 0; 24 | }; 25 | 26 | } // namespace arduino -------------------------------------------------------------------------------- /extras/test/cores/esp32/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | add_library(Esp32Core 6 | EEPROM.cpp 7 | EEPROM.h 8 | Stream.h 9 | ) 10 | 11 | target_include_directories(Esp32Core 12 | PUBLIC 13 | ${CMAKE_CURRENT_SOURCE_DIR} 14 | ${CMAKE_CURRENT_SOURCE_DIR}/../avr 15 | ) 16 | 17 | target_compile_definitions(Esp32Core 18 | PUBLIC 19 | ARDUINO_ARCH_ESP32 20 | ESP_ARDUINO_VERSION_MAJOR=2 21 | ESP_ARDUINO_VERSION_MINOR=0 22 | ESP_ARDUINO_VERSION_PATCH=5 23 | ) 24 | 25 | target_compile_options(Esp32Core 26 | PUBLIC 27 | -funsigned-char 28 | ) 29 | 30 | add_streamutils_test(StreamUtilsTestEsp32 Esp32Core) -------------------------------------------------------------------------------- /src/StreamUtils/Clients/SpyingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectSpyingPolicy.hpp" 8 | #include "../Policies/ReadSpyingPolicy.hpp" 9 | #include "../Policies/WriteSpyingPolicy.hpp" 10 | #include "ClientProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct SpyingClient 15 | : ClientProxy { 16 | SpyingClient(Client &target, Print &log) 17 | : ClientProxy( 18 | target, {log}, {log}, {log}) {} 19 | }; 20 | 21 | } // namespace StreamUtils 22 | -------------------------------------------------------------------------------- /extras/test/HammingClientTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "SpyingAllocator.hpp" 6 | 7 | #include "StreamUtils/Clients/HammingClient.hpp" 8 | #include "StreamUtils/Clients/MemoryClient.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | using namespace StreamUtils; 13 | 14 | TEST_CASE("HammingClient") { 15 | MemoryClient upstream(64); 16 | 17 | HammingClient<7, 4> client{upstream}; 18 | 19 | SUBCASE("read() decodes") { 20 | upstream.print("Tq"); 21 | 22 | CHECK(client.read() == 'A'); 23 | } 24 | 25 | SUBCASE("write() encodes") { 26 | client.write('A'); 27 | 28 | CHECK(upstream.readString() == "Tq"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /extras/test/HammingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "SpyingAllocator.hpp" 6 | 7 | #include "StreamUtils/Streams/HammingStream.hpp" 8 | #include "StreamUtils/Streams/MemoryStream.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | using namespace StreamUtils; 13 | 14 | TEST_CASE("HammingStream") { 15 | MemoryStream upstream(64); 16 | 17 | HammingStream<7, 4> stream{upstream}; 18 | 19 | SUBCASE("read() decodes") { 20 | upstream.print("Tq"); 21 | 22 | CHECK(stream.read() == 'A'); 23 | } 24 | 25 | SUBCASE("write() encodes") { 26 | stream.write('A'); 27 | 28 | CHECK(upstream.readString() == "Tq"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/HammingEncodingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/HammingEncodingPolicy.hpp" 8 | #include "../Policies/ReadForwardingPolicy.hpp" 9 | #include "../Ports/DefaultAllocator.hpp" 10 | #include "StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | using BasicHammingEncodingStream = 16 | StreamProxy>; 17 | 18 | template 19 | using HammingEncodingStream = 20 | BasicHammingEncodingStream; 21 | 22 | } // namespace StreamUtils 23 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/HammingDecodingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/HammingDecodingPolicy.hpp" 8 | #include "../Policies/WriteForwardingPolicy.hpp" 9 | #include "../Ports/DefaultAllocator.hpp" 10 | #include "StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | using BasicHammingDecodingStream = 16 | StreamProxy, WriteForwardingPolicy>; 17 | 18 | template 19 | using HammingDecodingStream = 20 | BasicHammingDecodingStream; 21 | 22 | } // namespace StreamUtils 23 | -------------------------------------------------------------------------------- /extras/test/cores/stm32f1/Client.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Stream.h" 8 | #include "WString.h" 9 | 10 | using IPAddress = String; 11 | 12 | class Client : public Stream { 13 | public: 14 | virtual int connect(IPAddress ip, uint16_t port) = 0; 15 | virtual int connect(const char *host, uint16_t port) = 0; 16 | virtual uint8_t connected() = 0; 17 | virtual void stop() = 0; 18 | virtual operator bool() = 0; 19 | 20 | virtual int read() = 0; 21 | virtual int read(uint8_t *buf, size_t size) = 0; 22 | 23 | virtual size_t write(uint8_t) = 0; 24 | virtual size_t write(const uint8_t *buf, size_t size) = 0; 25 | }; 26 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/HammingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/HammingDecodingPolicy.hpp" 8 | #include "../Policies/HammingEncodingPolicy.hpp" 9 | #include "../Ports/DefaultAllocator.hpp" 10 | #include "StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | using BasicHammingStream = StreamProxy, 16 | HammingEncodingPolicy>; 17 | 18 | template 19 | using HammingStream = BasicHammingStream; 20 | 21 | } // namespace StreamUtils 22 | -------------------------------------------------------------------------------- /extras/test/HammingEncodingClientTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "SpyingAllocator.hpp" 6 | 7 | #include "StreamUtils/Clients/HammingEncodingClient.hpp" 8 | #include "StreamUtils/Clients/MemoryClient.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | using namespace StreamUtils; 13 | 14 | TEST_CASE("HammingEncodingClient") { 15 | MemoryClient upstream(64); 16 | 17 | HammingEncodingClient<7, 4> client{upstream}; 18 | 19 | SUBCASE("read() forwards upstream data") { 20 | upstream.print("A"); 21 | 22 | CHECK(client.read() == 'A'); 23 | } 24 | 25 | SUBCASE("write() encodes") { 26 | client.write('A'); 27 | 28 | CHECK(upstream.readString() == "Tq"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /extras/test/HammingEncodingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "SpyingAllocator.hpp" 6 | 7 | #include "StreamUtils/Streams/HammingEncodingStream.hpp" 8 | #include "StreamUtils/Streams/MemoryStream.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | using namespace StreamUtils; 13 | 14 | TEST_CASE("HammingEncodingStream") { 15 | MemoryStream upstream(64); 16 | 17 | HammingEncodingStream<7, 4> stream{upstream}; 18 | 19 | SUBCASE("read() forwards upstream data") { 20 | upstream.print("A"); 21 | 22 | CHECK(stream.read() == 'A'); 23 | } 24 | 25 | SUBCASE("write() encodes") { 26 | stream.write('A'); 27 | 28 | CHECK(upstream.readString() == "Tq"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/ChunkDecodingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "StreamUtils/Policies/ChunkDecodingPolicy.hpp" 8 | #include "StreamUtils/Policies/WriteForwardingPolicy.hpp" 9 | 10 | #include "StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct ChunkDecodingStream 15 | : StreamProxy { 16 | ChunkDecodingStream(Stream &target) 17 | : StreamProxy(target) {} 18 | 19 | bool error() const { 20 | return _reader.error(); 21 | } 22 | 23 | bool ended() const { 24 | return _reader.ended(); 25 | } 26 | }; 27 | 28 | } // namespace StreamUtils 29 | -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ReadForwardingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace StreamUtils { 10 | 11 | struct ReadForwardingPolicy { 12 | int available(Stream &target) { 13 | return target.available(); 14 | } 15 | 16 | int read(Stream &target) { 17 | return target.read(); 18 | } 19 | 20 | int peek(Stream &target) { 21 | return target.peek(); 22 | } 23 | 24 | size_t readBytes(Stream &target, char *buffer, size_t size) { 25 | return target.readBytes(buffer, size); 26 | } 27 | 28 | int read(Client &target, uint8_t *buffer, size_t size) { 29 | return target.read(buffer, size); 30 | } 31 | }; 32 | 33 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Clients/HammingDecodingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/HammingDecodingPolicy.hpp" 9 | #include "../Policies/WriteForwardingPolicy.hpp" 10 | #include "../Ports/DefaultAllocator.hpp" 11 | #include "ClientProxy.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | using BasicHammingDecodingClient = 17 | ClientProxy, WriteForwardingPolicy, 18 | ConnectForwardingPolicy>; 19 | 20 | template 21 | using HammingDecodingClient = 22 | BasicHammingDecodingClient; 23 | 24 | } // namespace StreamUtils 25 | -------------------------------------------------------------------------------- /src/StreamUtils/Clients/HammingEncodingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/HammingEncodingPolicy.hpp" 9 | #include "../Policies/ReadForwardingPolicy.hpp" 10 | #include "../Ports/DefaultAllocator.hpp" 11 | #include "ClientProxy.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | using BasicHammingEncodingClient = 17 | ClientProxy, 18 | ConnectForwardingPolicy>; 19 | 20 | template 21 | using HammingEncodingClient = 22 | BasicHammingEncodingClient; 23 | 24 | } // namespace StreamUtils 25 | -------------------------------------------------------------------------------- /extras/test/cores/avr/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | struct Stream : Print { 10 | virtual int available() = 0; 11 | virtual int read() = 0; 12 | virtual int peek() = 0; 13 | 14 | size_t readBytes(char *buffer, size_t length) { 15 | size_t count = 0; 16 | while (count < length) { 17 | int c = read(); 18 | if (c < 0) 19 | break; 20 | *buffer++ = (char)c; 21 | count++; 22 | } 23 | return count; 24 | } 25 | 26 | String readString() { 27 | String result; 28 | int c; 29 | while ((c = read()) >= 0) { 30 | result += static_cast(c); 31 | } 32 | return result; 33 | } 34 | 35 | void setTimeout(unsigned long) {} 36 | }; 37 | -------------------------------------------------------------------------------- /src/StreamUtils/Prints/BufferingPrint.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/WriteBufferingPolicy.hpp" 8 | #include "../Ports/DefaultAllocator.hpp" 9 | #include "PrintProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | template 14 | struct BasicBufferingPrint : PrintProxy> { 15 | explicit BasicBufferingPrint(Print &upstream, size_t capacity, 16 | TAllocator allocator = TAllocator()) 17 | : PrintProxy>(upstream, 18 | {capacity, allocator}) {} 19 | }; 20 | 21 | using BufferingPrint = BasicBufferingPrint; 22 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/stm32/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Print.h" 8 | 9 | struct Stream : Print { 10 | virtual int available() = 0; 11 | virtual int read() = 0; 12 | virtual int peek() = 0; 13 | 14 | virtual size_t readBytes(char *buffer, size_t length) { 15 | size_t count = 0; 16 | while (count < length) { 17 | int c = read(); 18 | if (c < 0) 19 | break; 20 | *buffer++ = (char)c; 21 | count++; 22 | } 23 | return count; 24 | } 25 | 26 | String readString() { 27 | String result; 28 | int c; 29 | while ((c = read()) >= 0) { 30 | result += static_cast(c); 31 | } 32 | return result; 33 | } 34 | 35 | void setTimeout(unsigned long) {} 36 | }; 37 | -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ConnectForwardingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Configuration.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | struct ConnectForwardingPolicy { 14 | int connect(Client& target, const IPAddress& ip, uint16_t port) { 15 | return target.connect(ip, port); 16 | } 17 | 18 | int connect(Client& target, const char* ip, uint16_t port) { 19 | return target.connect(ip, port); 20 | } 21 | 22 | uint8_t connected(Client& target) { 23 | return target.connected(); 24 | } 25 | 26 | void stop(Client& target) { 27 | target.stop(); 28 | } 29 | 30 | bool operator_bool(Client& target) { 31 | return target.operator bool(); 32 | } 33 | }; 34 | 35 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Clients/HammingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/HammingDecodingPolicy.hpp" 9 | #include "../Policies/HammingEncodingPolicy.hpp" 10 | #include "../Ports/DefaultAllocator.hpp" 11 | #include "ClientProxy.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | using BasicHammingClient = ClientProxy, 17 | HammingEncodingPolicy, 18 | ConnectForwardingPolicy>; 19 | 20 | template 21 | using HammingClient = BasicHammingClient; 22 | 23 | } // namespace StreamUtils 24 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/WriteWaitingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadForwardingPolicy.hpp" 8 | #include "../Policies/WriteWaitingPolicy.hpp" 9 | #include "StreamProxy.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | struct WriteWaitingStream 14 | : StreamProxy { 15 | WriteWaitingStream(Stream &target, Polyfills::function wait = yield) 16 | : StreamProxy( 17 | target, ReadForwardingPolicy{}, 18 | WriteWaitingPolicy{Polyfills::move(wait)}) {} 19 | 20 | void setTimeout(unsigned long timeout) { 21 | Stream::setTimeout(timeout); 22 | _writer.setTimeout(timeout); 23 | } 24 | }; 25 | 26 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/stm32f1/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Print.h" 8 | 9 | struct Stream : Print { 10 | virtual int available() = 0; 11 | virtual int read() = 0; 12 | virtual int peek() = 0; 13 | virtual void flush() = 0; 14 | 15 | size_t readBytes(char *buffer, size_t length) { 16 | size_t count = 0; 17 | while (count < length) { 18 | int c = read(); 19 | if (c < 0) 20 | break; 21 | *buffer++ = (char)c; 22 | count++; 23 | } 24 | return count; 25 | } 26 | 27 | String readString() { 28 | String result; 29 | int c; 30 | while ((c = read()) >= 0) { 31 | result += static_cast(c); 32 | } 33 | return result; 34 | } 35 | 36 | void setTimeout(unsigned long) {} 37 | }; 38 | -------------------------------------------------------------------------------- /extras/test/cores/stm32f1/Print.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | typedef uint32_t uint32; 13 | 14 | struct Print { 15 | virtual size_t write(const void *buf, uint32 len) = 0; 16 | virtual size_t write(uint8_t data) = 0; 17 | 18 | size_t print(const String &s) { 19 | return write(s.c_str(), s.length()); 20 | } 21 | 22 | size_t print(const char *s) { 23 | return write(s, std::strlen(s)); 24 | } 25 | 26 | size_t println() { 27 | return 0; 28 | } 29 | 30 | template 31 | size_t print(const T &value) { 32 | return print(String(value)); 33 | } 34 | 35 | template 36 | size_t println(const T &value) { 37 | return print(value); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /extras/test/cores/coreapi/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Print.h" 8 | 9 | namespace arduino { 10 | 11 | struct Stream : Print { 12 | virtual int available() = 0; 13 | virtual int read() = 0; 14 | virtual int peek() = 0; 15 | 16 | size_t readBytes(char *buffer, size_t length) { 17 | size_t count = 0; 18 | while (count < length) { 19 | int c = read(); 20 | if (c < 0) 21 | break; 22 | *buffer++ = (char)c; 23 | count++; 24 | } 25 | return count; 26 | } 27 | 28 | String readString() { 29 | String result; 30 | int c; 31 | while ((c = read()) >= 0) { 32 | result += static_cast(c); 33 | } 34 | return result; 35 | } 36 | 37 | void setTimeout(unsigned long) {} 38 | }; 39 | 40 | } // namespace arduino -------------------------------------------------------------------------------- /src/StreamUtils/Clients/LoggingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/ReadLoggingPolicy.hpp" 9 | #include "../Policies/WriteLoggingPolicy.hpp" 10 | #include "ClientProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct LoggingClient : ClientProxy { 16 | LoggingClient(Client &target, Print &log) 17 | : ClientProxy(target, ReadLoggingPolicy{log}, 19 | WriteLoggingPolicy{log}, 20 | ConnectForwardingPolicy{}) {} 21 | }; 22 | 23 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/SpyingAllocator.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "StreamUtils/Ports/DefaultAllocator.hpp" 10 | 11 | class SpyingAllocator { 12 | public: 13 | SpyingAllocator(Print& log) : _log(&log) {} 14 | 15 | bool forceFail = false; 16 | 17 | void* allocate(size_t n) { 18 | void* ptr = forceFail ? 0 : _allocator.allocate(n); 19 | _log->print("allocate("); 20 | _log->print(n); 21 | _log->print(") -> "); 22 | _log->print(ptr ? "ptr" : "null"); 23 | return ptr; 24 | } 25 | 26 | void deallocate(void* ptr) { 27 | _log->print("deallocate("); 28 | _log->print(ptr ? "ptr" : "null"); 29 | _log->print(")"); 30 | _allocator.deallocate(ptr); 31 | } 32 | 33 | private: 34 | Print* _log; 35 | StreamUtils::DefaultAllocator _allocator; 36 | }; 37 | -------------------------------------------------------------------------------- /src/StreamUtils/Clients/WriteLoggingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/ReadForwardingPolicy.hpp" 9 | #include "../Policies/WriteLoggingPolicy.hpp" 10 | #include "ClientProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct WriteLoggingClient 15 | : ClientProxy { 17 | WriteLoggingClient(Client &target, Print &log) 18 | : ClientProxy(target, ReadForwardingPolicy{}, 20 | WriteLoggingPolicy{log}, 21 | ConnectForwardingPolicy{}) {} 22 | }; 23 | 24 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Clients/ChunkDecodingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "StreamUtils/Policies/ChunkDecodingPolicy.hpp" 8 | #include "StreamUtils/Policies/ConnectForwardingPolicy.hpp" 9 | #include "StreamUtils/Policies/WriteForwardingPolicy.hpp" 10 | 11 | #include "ClientProxy.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | struct ChunkDecodingClient 16 | : ClientProxy { 18 | ChunkDecodingClient(Client &target) 19 | : ClientProxy(target) {} 21 | 22 | bool error() const { 23 | return _reader.error(); 24 | } 25 | 26 | bool ended() const { 27 | return _reader.ended(); 28 | } 29 | }; 30 | 31 | } // namespace StreamUtils 32 | -------------------------------------------------------------------------------- /src/StreamUtils/Clients/ReadLoggingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/ReadLoggingPolicy.hpp" 9 | #include "../Policies/WriteForwardingPolicy.hpp" 10 | #include "ClientProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct ReadLoggingClient : ClientProxy { 16 | ReadLoggingClient(Client &target, Print &log) 17 | : ClientProxy(target, ReadLoggingPolicy{log}, 19 | WriteForwardingPolicy{}, 20 | ConnectForwardingPolicy{}) {} 21 | }; 22 | 23 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Policies/WriteLoggingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "../Configuration.hpp" 9 | 10 | namespace StreamUtils { 11 | 12 | class WriteLoggingPolicy { 13 | public: 14 | WriteLoggingPolicy(Print &log) : _log(log) {} 15 | 16 | size_t write(Print &target, const uint8_t *buffer, size_t size) { 17 | size_t result = target.write(buffer, size); 18 | _log.write(buffer, result); 19 | return result; 20 | } 21 | 22 | size_t write(Print &target, uint8_t c) { 23 | size_t result = target.write(c); 24 | _log.write(c); 25 | return result; 26 | } 27 | 28 | template 29 | void flush(TTarget &target) { 30 | target.flush(); 31 | } 32 | 33 | void implicitFlush(Print &) {} 34 | 35 | private: 36 | Print &_log; 37 | }; 38 | 39 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Streams/WriteBufferingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadForwardingPolicy.hpp" 8 | #include "../Policies/WriteBufferingPolicy.hpp" 9 | #include "../Ports/DefaultAllocator.hpp" 10 | #include "StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | struct BasicWriteBufferingStream 16 | : StreamProxy> { 17 | explicit BasicWriteBufferingStream(Stream &upstream, size_t capacity, 18 | TAllocator allocator = TAllocator()) 19 | : StreamProxy>( 20 | upstream, {}, {capacity, allocator}) {} 21 | }; 22 | 23 | using WriteBufferingStream = BasicWriteBufferingStream; 24 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/images/StringPrint.svg: -------------------------------------------------------------------------------- 1 | String -------------------------------------------------------------------------------- /extras/images/ProgmemStream.svg: -------------------------------------------------------------------------------- 1 | PROGMEM -------------------------------------------------------------------------------- /extras/test/cores/esp8266/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Print.h" 8 | 9 | struct Stream : Print { 10 | virtual int available() = 0; 11 | virtual int read() = 0; 12 | virtual int peek() = 0; 13 | 14 | virtual size_t readBytes(char *buffer, size_t length) { 15 | size_t count = 0; 16 | while (count < length) { 17 | int c = read(); 18 | if (c < 0) 19 | break; 20 | *buffer++ = (char)c; 21 | count++; 22 | } 23 | return count; 24 | } 25 | 26 | virtual size_t readBytes(uint8_t *buffer, size_t length) { 27 | return readBytes((char *)buffer, length); 28 | } 29 | 30 | virtual String readString() { 31 | String result; 32 | int c; 33 | while ((c = read()) >= 0) { 34 | result += static_cast(c); 35 | } 36 | return result; 37 | } 38 | 39 | void setTimeout(unsigned long) {} 40 | }; 41 | -------------------------------------------------------------------------------- /extras/test/cores/nrf52/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Print.h" 8 | 9 | struct Stream : Print { 10 | virtual int available() = 0; 11 | virtual int read() = 0; 12 | virtual int peek() = 0; 13 | virtual void flush() {} 14 | 15 | size_t readBytes(char *buffer, size_t length) { 16 | size_t count = 0; 17 | while (count < length) { 18 | int c = read(); 19 | if (c < 0) 20 | break; 21 | *buffer++ = (char)c; 22 | count++; 23 | } 24 | return count; 25 | } 26 | 27 | size_t readBytes(uint8_t *buffer, size_t length) { 28 | return readBytes((char *)buffer, length); 29 | } 30 | 31 | String readString() { 32 | String result; 33 | int c; 34 | while ((c = read()) >= 0) { 35 | result += static_cast(c); 36 | } 37 | return result; 38 | } 39 | 40 | void setTimeout(unsigned long) {} 41 | }; 42 | -------------------------------------------------------------------------------- /extras/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | # Copyright Benoit Blanchon 2019-2024 3 | # MIT License 4 | 5 | if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") 6 | add_compile_options(-Wall -Wextra -Werror -Wundef) 7 | add_link_options(-static) 8 | endif() 9 | 10 | file(GLOB SOURCES CONFIGURE_DEPENDS *.cpp) 11 | 12 | add_subdirectory(doctest) 13 | 14 | function(add_streamutils_test EXECUTABLE CORE) 15 | add_executable(${EXECUTABLE} ${SOURCES}) 16 | target_link_libraries(${EXECUTABLE} ${CORE} doctest) 17 | add_test(${EXECUTABLE} ${EXECUTABLE}) 18 | endfunction() 19 | 20 | add_subdirectory(cores/avr) 21 | add_subdirectory(cores/dxcore) 22 | add_subdirectory(cores/esp32) 23 | add_subdirectory(cores/esp8266) 24 | add_subdirectory(cores/mbed) 25 | add_subdirectory(cores/nrf52) 26 | add_subdirectory(cores/rp2040) 27 | add_subdirectory(cores/samd) 28 | add_subdirectory(cores/stm32) 29 | add_subdirectory(cores/stm32f1) 30 | add_subdirectory(cores/stm32f4) 31 | add_subdirectory(cores/teensy) -------------------------------------------------------------------------------- /extras/test/cores/esp32/Stream.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "Print.h" 8 | 9 | struct Stream : Print { 10 | virtual ~Stream() {} 11 | 12 | virtual int available() = 0; 13 | virtual int read() = 0; 14 | virtual int peek() = 0; 15 | 16 | virtual size_t readBytes(char *buffer, size_t length) { 17 | size_t count = 0; 18 | while (count < length) { 19 | int c = read(); 20 | if (c < 0) 21 | break; 22 | *buffer++ = (char)c; 23 | count++; 24 | } 25 | return count; 26 | } 27 | 28 | virtual size_t readBytes(uint8_t *buffer, size_t length) { 29 | return readBytes((char *)buffer, length); 30 | } 31 | 32 | virtual String readString() { 33 | String result; 34 | int c; 35 | while ((c = read()) >= 0) { 36 | result += static_cast(c); 37 | } 38 | return result; 39 | } 40 | 41 | void setTimeout(unsigned long) {} 42 | }; 43 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/ReadBufferingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadBufferingPolicy.hpp" 8 | #include "../Policies/WriteForwardingPolicy.hpp" 9 | #include "../Ports/DefaultAllocator.hpp" 10 | #include "StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | class BasicReadBufferingStream 16 | : public StreamProxy, 17 | WriteForwardingPolicy> { 18 | using base_type = 19 | StreamProxy, WriteForwardingPolicy>; 20 | 21 | public: 22 | explicit BasicReadBufferingStream(Stream &upstream, size_t capacity, 23 | TAllocator allocator = TAllocator()) 24 | : base_type(upstream, {capacity, allocator}, {}) {} 25 | }; 26 | 27 | using ReadBufferingStream = BasicReadBufferingStream; 28 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/nrf52/Print.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | struct Print { 13 | virtual size_t write(const uint8_t *buffer, size_t size) = 0; 14 | virtual size_t write(uint8_t data) = 0; 15 | 16 | virtual int availableForWrite() { 17 | return 0; 18 | } 19 | 20 | size_t write(const char *buffer, size_t size) { 21 | return write((const uint8_t *)buffer, size); 22 | } 23 | 24 | size_t print(const String &s) { 25 | return write(s.c_str(), s.length()); 26 | } 27 | 28 | size_t print(const char *s) { 29 | return write(s, std::strlen(s)); 30 | } 31 | 32 | size_t println() { 33 | return 0; 34 | } 35 | 36 | template 37 | size_t print(const T &value) { 38 | return print(String(value)); 39 | } 40 | 41 | template 42 | size_t println(const T &value) { 43 | return print(value); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /src/StreamUtils/Clients/WriteWaitingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/ReadForwardingPolicy.hpp" 9 | #include "../Policies/WriteWaitingPolicy.hpp" 10 | #include "ClientProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | struct WriteWaitingClient 15 | : ClientProxy { 17 | WriteWaitingClient(Client &target, Polyfills::function wait = yield) 18 | : ClientProxy( 20 | target, ReadForwardingPolicy{}, 21 | WriteWaitingPolicy{Polyfills::move(wait)}, 22 | ConnectForwardingPolicy{}) {} 23 | 24 | void setTimeout(unsigned long timeout) { 25 | Client::setTimeout(timeout); 26 | _writer.setTimeout(timeout); 27 | } 28 | }; 29 | 30 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/avr/Print.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | struct Print { 13 | virtual size_t write(const uint8_t *buffer, size_t size) = 0; 14 | virtual size_t write(uint8_t data) = 0; 15 | virtual void flush() {} 16 | 17 | virtual int availableForWrite() { 18 | return 0; 19 | } 20 | 21 | size_t write(const char *buffer, size_t size) { 22 | return write((const uint8_t *)buffer, size); 23 | } 24 | 25 | size_t print(const String &s) { 26 | return write(s.c_str(), s.length()); 27 | } 28 | 29 | size_t print(const char *s) { 30 | return write(s, std::strlen(s)); 31 | } 32 | 33 | size_t println() { 34 | return 0; 35 | } 36 | 37 | template 38 | size_t print(const T &value) { 39 | return print(String(value)); 40 | } 41 | 42 | template 43 | size_t println(const T &value) { 44 | return print(value); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | --------------------- 3 | 4 | Copyright © 2019 Benoit BLANCHON 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ReadLoggingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace StreamUtils { 10 | 11 | class ReadLoggingPolicy { 12 | public: 13 | ReadLoggingPolicy(Print &log) : _log(log) {} 14 | 15 | int available(Stream &stream) { 16 | return stream.available(); 17 | } 18 | 19 | int read(Stream &stream) { 20 | int result = stream.read(); 21 | if (result >= 0) 22 | _log.write(result); 23 | return result; 24 | } 25 | 26 | int peek(Stream &stream) { 27 | return stream.peek(); 28 | } 29 | 30 | size_t readBytes(Stream &stream, char *buffer, size_t size) { 31 | size_t result = stream.readBytes(buffer, size); 32 | _log.write(buffer, result); 33 | return result; 34 | } 35 | 36 | int read(Client &client, uint8_t *buffer, size_t size) { 37 | int result = client.read(buffer, size); 38 | _log.write(buffer, result); 39 | return result; 40 | } 41 | 42 | private: 43 | Print &_log; 44 | }; 45 | 46 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ReadThrottlingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace StreamUtils { 10 | 11 | template 12 | struct ReadThrottlingPolicy { 13 | ReadThrottlingPolicy(TThrottler throttler) : _throttler(throttler) {} 14 | 15 | int available(Stream &stream) { 16 | return stream.available(); 17 | } 18 | 19 | int read(Stream &stream) { 20 | _throttler.throttle(); 21 | return stream.read(); 22 | } 23 | 24 | int peek(Stream &stream) { 25 | _throttler.throttle(); 26 | return stream.peek(); 27 | } 28 | 29 | size_t readBytes(Stream &stream, char *buffer, size_t size) { 30 | for (size_t i = 0; i < size; i++) { 31 | int c = read(stream); 32 | if (c < 0) 33 | return i; 34 | buffer[i] = c; 35 | } 36 | return size; 37 | } 38 | 39 | const TThrottler &throttler() const { 40 | return _throttler; 41 | } 42 | 43 | private: 44 | TThrottler _throttler; 45 | }; 46 | 47 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/LoggingPrintTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Prints/LoggingPrint.hpp" 6 | #include "StreamUtils/Streams/MemoryStream.hpp" 7 | 8 | #include "doctest.h" 9 | 10 | using namespace StreamUtils; 11 | 12 | TEST_CASE("LoggingPrint") { 13 | MemoryStream primary(4); 14 | MemoryStream secondary(64); 15 | LoggingPrint loggingPrint{primary, secondary}; 16 | 17 | SUBCASE("write(char)") { 18 | size_t n = loggingPrint.write('A'); 19 | 20 | CHECK(n == 1); 21 | CHECK(primary.readString() == "A"); 22 | CHECK(secondary.readString() == "A"); 23 | } 24 | 25 | SUBCASE("write(char*,size_t)") { 26 | size_t n = loggingPrint.write("ABCDEF", 6); 27 | 28 | CHECK(n == 4); 29 | CHECK(primary.readString() == "ABCD"); 30 | CHECK(secondary.readString() == "ABCD"); 31 | } 32 | 33 | #if STREAMUTILS_PRINT_FLUSH_EXISTS 34 | SUBCASE("flush()") { 35 | loggingPrint.write("AB", 2); 36 | REQUIRE(primary.available() == 2); 37 | loggingPrint.flush(); 38 | REQUIRE(primary.available() == 0); 39 | } 40 | #endif 41 | } 42 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/ReadThrottlingStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ReadThrottlingPolicy.hpp" 8 | #include "../Policies/WriteForwardingPolicy.hpp" 9 | #include "StreamProxy.hpp" 10 | 11 | #ifdef ARDUINO 12 | #include "../Ports/ArduinoThrottler.hpp" 13 | #endif 14 | 15 | namespace StreamUtils { 16 | 17 | template 18 | class BasicReadThrottlingStream 19 | : public StreamProxy, 20 | WriteForwardingPolicy> { 21 | public: 22 | BasicReadThrottlingStream(Stream& upstream, 23 | TThrottler throttler = TThrottler()) 24 | : StreamProxy, WriteForwardingPolicy>( 25 | upstream, ReadThrottlingPolicy(throttler), {}) {} 26 | 27 | const TThrottler& throttler() const { 28 | return this->_reader.throttler(); 29 | } 30 | }; 31 | 32 | #ifdef ARDUINO 33 | using ReadThrottlingStream = BasicReadThrottlingStream; 34 | #endif 35 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/coreapi/Print.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "WString.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace arduino { 13 | 14 | struct Print { 15 | virtual size_t write(const uint8_t *buffer, size_t size) = 0; 16 | virtual size_t write(uint8_t data) = 0; 17 | virtual void flush() {} 18 | 19 | virtual int availableForWrite() { 20 | return 0; 21 | } 22 | 23 | size_t write(const char *buffer, size_t size) { 24 | return write((const uint8_t *)buffer, size); 25 | } 26 | 27 | size_t print(const String &s) { 28 | return write(s.c_str(), s.length()); 29 | } 30 | 31 | size_t print(const char *s) { 32 | return write(s, std::strlen(s)); 33 | } 34 | 35 | size_t println() { 36 | return 0; 37 | } 38 | 39 | template 40 | size_t print(const T &value) { 41 | return print(String(value)); 42 | } 43 | 44 | template 45 | size_t println(const T &value) { 46 | return print(value); 47 | } 48 | }; 49 | 50 | } // namespace arduino 51 | 52 | using namespace arduino; -------------------------------------------------------------------------------- /src/StreamUtils/Clients/WriteBufferingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/ReadForwardingPolicy.hpp" 9 | #include "../Policies/WriteBufferingPolicy.hpp" 10 | #include "../Ports/DefaultAllocator.hpp" 11 | #include "ClientProxy.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | struct BasicWriteBufferingClient 17 | : ClientProxy, 18 | ConnectForwardingPolicy> { 19 | explicit BasicWriteBufferingClient(Client &target, size_t capacity, 20 | TAllocator allocator = TAllocator()) 21 | : ClientProxy, 22 | ConnectForwardingPolicy>( 23 | target, ReadForwardingPolicy{}, 24 | WriteBufferingPolicy{capacity, allocator}, 25 | ConnectForwardingPolicy{}) {} 26 | }; 27 | 28 | using WriteBufferingClient = BasicWriteBufferingClient; 29 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Clients/ReadBufferingClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Policies/ConnectForwardingPolicy.hpp" 8 | #include "../Policies/ReadBufferingPolicy.hpp" 9 | #include "../Policies/WriteForwardingPolicy.hpp" 10 | #include "../Ports/DefaultAllocator.hpp" 11 | #include "ClientProxy.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | class BasicReadBufferingClient 17 | : public ClientProxy, WriteForwardingPolicy, 18 | ConnectForwardingPolicy> { 19 | using base_type = ClientProxy, 20 | WriteForwardingPolicy, ConnectForwardingPolicy>; 21 | 22 | public: 23 | explicit BasicReadBufferingClient(Client &target, size_t capacity, 24 | TAllocator allocator = TAllocator()) 25 | : base_type(target, ReadBufferingPolicy{capacity, allocator}, 26 | WriteForwardingPolicy{}, ConnectForwardingPolicy{}) {} 27 | }; 28 | 29 | using ReadBufferingClient = BasicReadBufferingClient; 30 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/images/EepromStream.svg: -------------------------------------------------------------------------------- 1 | EEPROM -------------------------------------------------------------------------------- /extras/images/StringStream.svg: -------------------------------------------------------------------------------- 1 | String -------------------------------------------------------------------------------- /src/StreamUtils/Buffers/CharArray.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include // size_t 9 | #include // memcpy 10 | 11 | namespace StreamUtils { 12 | 13 | template 14 | class CharArray { 15 | public: 16 | CharArray(size_t size, TAllocator allocator = TAllocator()) 17 | : _allocator(allocator) { 18 | _data = reinterpret_cast(_allocator.allocate(size)); 19 | _size = _data ? size : 0; 20 | } 21 | 22 | CharArray(const CharArray &src) : CharArray(src._size, src._allocator) { 23 | if (_data != nullptr) 24 | memcpy(_data, src._data, _size); 25 | } 26 | 27 | ~CharArray() { 28 | _allocator.deallocate(_data); 29 | } 30 | 31 | size_t size() const { 32 | return _size; 33 | } 34 | 35 | operator bool() const { 36 | return _size > 0; 37 | } 38 | 39 | char *operator&() { 40 | return _data; 41 | } 42 | 43 | char &operator[](size_t i) { 44 | return _data[i]; 45 | } 46 | 47 | char operator[](size_t i) const { 48 | return _data[i]; 49 | } 50 | 51 | protected: 52 | TAllocator _allocator; 53 | char *_data; 54 | size_t _size; 55 | }; 56 | 57 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Policies/WriteSpyingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Configuration.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | class WriteSpyingPolicy { 14 | public: 15 | WriteSpyingPolicy(Print &log) : _log(log) {} 16 | 17 | size_t write(Print &stream, const uint8_t *buffer, size_t size) { 18 | _log.print("write('"); 19 | for (size_t i = 0; i < size; i++) { 20 | _log.write(buffer[i]); 21 | } 22 | _log.print("', "); 23 | _log.print(size); 24 | _log.print(") -> "); 25 | 26 | size_t result = stream.write(buffer, size); 27 | _log.println(result); 28 | 29 | return result; 30 | } 31 | 32 | size_t write(Print &stream, uint8_t data) { 33 | _log.print("write('"); 34 | _log.write(data); 35 | _log.print("') -> "); 36 | 37 | size_t result = stream.write(data); 38 | _log.println(result); 39 | 40 | return result; 41 | } 42 | 43 | template 44 | void flush(TTarget &target) { 45 | _log.println("flush()"); 46 | target.flush(); 47 | } 48 | 49 | void implicitFlush(Print &) {} 50 | 51 | private: 52 | Print &_log; 53 | }; 54 | 55 | } // namespace StreamUtils 56 | -------------------------------------------------------------------------------- /src/StreamUtils/Prints/StringPrint.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "../Configuration.hpp" 11 | #include "../Polyfills.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | class StringPrint : public Print { 16 | public: 17 | StringPrint() {} 18 | 19 | explicit StringPrint(String str) : _str(Polyfills::move(str)) {} 20 | 21 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 22 | size_t write(const void* data, uint32 n) override { 23 | const uint8_t* p = reinterpret_cast(data); 24 | #else 25 | size_t write(const uint8_t* p, size_t n) override { 26 | #endif 27 | for (size_t i = 0; i < n; i++) { 28 | uint8_t c = p[i]; 29 | if (c == 0) 30 | return i; 31 | write(c); 32 | } 33 | return n; 34 | } 35 | 36 | size_t write(uint8_t c) override { 37 | if (c == 0) 38 | return 0; 39 | _str += static_cast(c); 40 | return 1; 41 | } 42 | 43 | const String& str() const { 44 | return _str; 45 | } 46 | 47 | void str(String str) { 48 | _str = Polyfills::move(str); 49 | } 50 | 51 | void clear() { 52 | _str = ""; 53 | } 54 | 55 | private: 56 | String _str; 57 | }; 58 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/avr/WString.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | class __FlashStringHelper; 11 | #define F(s) (reinterpret_cast(s + 42)) 12 | 13 | class String : private std::string { 14 | public: 15 | String() {} 16 | String(const String& s) : std::string(s) {} 17 | String(String&& s) : std::string(std::move(s)) {} 18 | String(const char* s) : std::string(s) {} 19 | String(int n) : std::string(std::to_string(n)) {} 20 | String(size_t n) : std::string(std::to_string(n)) {} 21 | 22 | String& operator=(const String& rhs) { 23 | std::string::operator=(rhs); 24 | return *this; 25 | } 26 | 27 | using std::string::c_str; 28 | using std::string::length; 29 | using std::string::operator+=; 30 | using std::string::operator[]; 31 | 32 | void remove(unsigned int index, unsigned int count) { 33 | erase(begin() + index, begin() + index + count); 34 | } 35 | 36 | friend bool operator==(const String& lhs, const char* rhs) { 37 | return static_cast(lhs) == rhs; 38 | } 39 | 40 | friend std::ostream& operator<<(std::ostream& lhs, const String& rhs) { 41 | return lhs << static_cast(rhs); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/StreamUtils/Polyfills.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | namespace StreamUtils { 8 | namespace Polyfills { 9 | 10 | template 11 | struct remove_reference { 12 | using type = T; 13 | }; 14 | 15 | template 16 | struct remove_reference { 17 | using type = T; 18 | }; 19 | 20 | template 21 | typename remove_reference::type &&move(T &&t) { 22 | return static_cast::type &&>(t); 23 | } 24 | 25 | // poor man's std::function 26 | class function { 27 | struct callable_base { 28 | virtual void operator()() = 0; 29 | virtual ~callable_base() {} 30 | }; 31 | 32 | template 33 | struct callable : callable_base { 34 | Functor functor; 35 | callable(Functor functor) : functor(functor) {} 36 | virtual void operator()() { 37 | functor(); 38 | } 39 | }; 40 | 41 | callable_base *_callable; 42 | 43 | public: 44 | template 45 | function(Functor f) { 46 | _callable = new callable(f); 47 | } 48 | function(function &&src) { 49 | _callable = src._callable, src._callable = 0; 50 | } 51 | ~function() { 52 | delete _callable; 53 | } 54 | void operator()() const { 55 | (*_callable)(); 56 | } 57 | }; 58 | 59 | } // namespace Polyfills 60 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/EepromStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Streams/EepromStream.hpp" 6 | 7 | #if STREAMUTILS_ENABLE_EEPROM 8 | 9 | #include "doctest.h" 10 | 11 | using namespace StreamUtils; 12 | 13 | TEST_CASE("EepromStream") { 14 | SUBCASE("available()") { 15 | EepromStream s(42, 84); 16 | CHECK(s.available() == 84); 17 | } 18 | 19 | SUBCASE("write(uint8_t)") { 20 | EepromStream s(0, 2); 21 | CHECK(s.write('a') == 1); 22 | CHECK(s.write('b') == 1); 23 | CHECK(s.write('c') == 0); 24 | CHECK(s.write('d') == 0); 25 | s.flush(); 26 | CHECK(s.readString() == "ab"); 27 | } 28 | 29 | SUBCASE("write(const uint8_t *, size_t)") { 30 | EepromStream s(0, 5); 31 | CHECK(s.write("abc", 3) == 3); 32 | CHECK(s.write("def", 3) == 2); 33 | CHECK(s.write("ghi", 3) == 0); 34 | s.flush(); 35 | CHECK(s.readString() == "abcde"); 36 | } 37 | 38 | SUBCASE("read()") { 39 | EepromStream s(0, 2); 40 | s.write("ab", 2); 41 | CHECK(s.read() == 'a'); 42 | CHECK(s.read() == 'b'); 43 | CHECK(s.read() == -1); 44 | } 45 | 46 | SUBCASE("peek()") { 47 | EepromStream s(0, 2); 48 | s.write("ab", 2); 49 | CHECK(s.peek() == 'a'); 50 | CHECK(s.peek() == 'a'); 51 | s.read(); 52 | CHECK(s.peek() == 'b'); 53 | s.read(); 54 | CHECK(s.peek() == -1); 55 | } 56 | } 57 | #endif -------------------------------------------------------------------------------- /src/StreamUtils/Prints/PrintProxy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Configuration.hpp" 10 | #include "../Polyfills.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | class PrintProxy : public Print { 16 | public: 17 | explicit PrintProxy(Print &upstream, WritePolicy writer = WritePolicy{}) 18 | : _target(upstream), _writer(Polyfills::move(writer)) {} 19 | 20 | PrintProxy(const PrintProxy &other) 21 | : _target(other._target), _writer(other._writer) {} 22 | 23 | ~PrintProxy() { 24 | _writer.implicitFlush(_target); 25 | } 26 | 27 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 28 | size_t write(const void *data, uint32 size) override { 29 | const uint8_t *buffer = reinterpret_cast(data); 30 | #else 31 | size_t write(const uint8_t *buffer, size_t size) override { 32 | #endif 33 | return _writer.write(_target, buffer, size); 34 | } 35 | 36 | size_t write(uint8_t data) override { 37 | return _writer.write(_target, data); 38 | } 39 | 40 | #if STREAMUTILS_PRINT_FLUSH_EXISTS 41 | void flush() override { 42 | #else 43 | void flush() { 44 | #endif 45 | _writer.flush(_target); 46 | } 47 | 48 | using Print::write; 49 | 50 | protected: 51 | Print &_target; 52 | WritePolicy _writer; 53 | }; 54 | 55 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/cores/coreapi/WString.h: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "../avr/avr/pgmspace.h" 11 | 12 | namespace arduino { 13 | 14 | class __FlashStringHelper; 15 | #define F(s) (reinterpret_cast(s + 42)) 16 | 17 | class String : private std::string { 18 | public: 19 | String() {} 20 | String(const String& s) : std::string(s) {} 21 | String(String&& s) : std::string(std::move(s)) {} 22 | String(const char* s) : std::string(s) {} 23 | String(int n) : std::string(std::to_string(n)) {} 24 | String(size_t n) : std::string(std::to_string(n)) {} 25 | 26 | String& operator=(const String& rhs) { 27 | std::string::operator=(rhs); 28 | return *this; 29 | } 30 | 31 | using std::string::c_str; 32 | using std::string::length; 33 | using std::string::operator+=; 34 | using std::string::operator[]; 35 | 36 | void remove(unsigned int index, unsigned int count) { 37 | erase(begin() + index, begin() + index + count); 38 | } 39 | 40 | friend bool operator==(const String& lhs, const char* rhs) { 41 | return static_cast(lhs) == rhs; 42 | } 43 | 44 | friend std::ostream& operator<<(std::ostream& lhs, const String& rhs) { 45 | return lhs << static_cast(rhs); 46 | } 47 | }; 48 | 49 | } // namespace arduino 50 | -------------------------------------------------------------------------------- /src/StreamUtils.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Clients/HammingClient.hpp" 6 | #include "StreamUtils/Clients/HammingDecodingClient.hpp" 7 | #include "StreamUtils/Clients/HammingEncodingClient.hpp" 8 | #include "StreamUtils/Clients/LoggingClient.hpp" 9 | #include "StreamUtils/Clients/ReadBufferingClient.hpp" 10 | #include "StreamUtils/Clients/ReadLoggingClient.hpp" 11 | #include "StreamUtils/Clients/WriteBufferingClient.hpp" 12 | #include "StreamUtils/Clients/WriteLoggingClient.hpp" 13 | #include "StreamUtils/Prints/BufferingPrint.hpp" 14 | #include "StreamUtils/Prints/HammingPrint.hpp" 15 | #include "StreamUtils/Prints/LoggingPrint.hpp" 16 | #include "StreamUtils/Prints/StringPrint.hpp" 17 | #include "StreamUtils/Streams/ChunkDecodingStream.hpp" 18 | #include "StreamUtils/Streams/EepromStream.hpp" 19 | #include "StreamUtils/Streams/HammingDecodingStream.hpp" 20 | #include "StreamUtils/Streams/HammingEncodingStream.hpp" 21 | #include "StreamUtils/Streams/HammingStream.hpp" 22 | #include "StreamUtils/Streams/LoggingStream.hpp" 23 | #include "StreamUtils/Streams/ProgmemStream.hpp" 24 | #include "StreamUtils/Streams/ReadBufferingStream.hpp" 25 | #include "StreamUtils/Streams/ReadLoggingStream.hpp" 26 | #include "StreamUtils/Streams/ReadThrottlingStream.hpp" 27 | #include "StreamUtils/Streams/StringStream.hpp" 28 | #include "StreamUtils/Streams/WriteBufferingStream.hpp" 29 | #include "StreamUtils/Streams/WriteLoggingStream.hpp" 30 | #include "StreamUtils/Streams/WriteWaitingStream.hpp" 31 | -------------------------------------------------------------------------------- /src/StreamUtils/Policies/WriteWaitingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Buffers/LinearBuffer.hpp" 10 | #include "../Configuration.hpp" 11 | #include "../Polyfills.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | struct WriteWaitingPolicy { 16 | public: 17 | WriteWaitingPolicy(Polyfills::function wait) 18 | : _wait(Polyfills::move(wait)), _timeout(1000) {} 19 | 20 | size_t write(Print &target, const uint8_t *data, size_t size) { 21 | unsigned long startTime = millis(); 22 | size_t totalWritten = 0; 23 | 24 | for (;;) { 25 | size_t n = target.write(data, size); 26 | size -= n; 27 | data += n; 28 | totalWritten += n; 29 | if (size == 0 || millis() - startTime >= _timeout) 30 | return totalWritten; 31 | _wait(); 32 | } 33 | } 34 | 35 | size_t write(Print &target, uint8_t data) { 36 | unsigned long startTime = millis(); 37 | 38 | for (;;) { 39 | if (target.write(data)) 40 | return 1; 41 | if (millis() - startTime >= _timeout) 42 | return 0; 43 | _wait(); 44 | } 45 | } 46 | 47 | template 48 | void flush(TTarget &target) { 49 | target.flush(); 50 | } 51 | 52 | void implicitFlush(Print &) {} 53 | 54 | void setTimeout(unsigned long timeout) { 55 | _timeout = timeout; 56 | } 57 | 58 | private: 59 | Polyfills::function _wait; 60 | unsigned long _timeout; 61 | }; 62 | 63 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ConnectSpyingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace StreamUtils { 10 | 11 | class ConnectSpyingPolicy { 12 | public: 13 | ConnectSpyingPolicy(Print& log) : _log(log) {} 14 | 15 | int connect(Client& target, const IPAddress& ip, uint16_t port) { 16 | _log.print("connect('"); 17 | _log.print(ip); 18 | _log.print("', "); 19 | _log.print(port); 20 | _log.print(") -> "); 21 | 22 | int result = target.connect(ip, port); 23 | _log.println(result); 24 | 25 | return result; 26 | } 27 | 28 | int connect(Client& target, const char* ip, uint16_t port) { 29 | _log.print("connect('"); 30 | _log.print(ip); 31 | _log.print("', "); 32 | _log.print(port); 33 | _log.print(") -> "); 34 | 35 | int result = target.connect(ip, port); 36 | _log.println(result); 37 | 38 | return result; 39 | } 40 | 41 | uint8_t connected(Client& target) { 42 | _log.print("connected() -> "); 43 | 44 | uint8_t result = target.connected(); 45 | _log.println(result); 46 | 47 | return result; 48 | } 49 | 50 | void stop(Client& target) { 51 | _log.print("stop()"); 52 | target.stop(); 53 | } 54 | 55 | bool operator_bool(Client& target) { 56 | _log.print("operator bool() -> "); 57 | 58 | bool result = target.operator bool(); 59 | _log.println(result ? "true" : "false"); 60 | 61 | return result; 62 | } 63 | 64 | private: 65 | Print& _log; 66 | }; 67 | 68 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ReadSpyingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace StreamUtils { 10 | 11 | class ReadSpyingPolicy { 12 | public: 13 | ReadSpyingPolicy(Print &log) : _log(log) {} 14 | 15 | int available(Stream &target) { 16 | int result = target.available(); 17 | _log.print("available() -> "); 18 | _log.println(result); 19 | return result; 20 | } 21 | 22 | int read(Stream &target) { 23 | int result = target.read(); 24 | _log.print("read() -> "); 25 | _log.println(result); 26 | return result; 27 | } 28 | 29 | int peek(Stream &target) { 30 | int result = target.peek(); 31 | _log.print("peek() -> "); 32 | _log.println(result); 33 | return result; 34 | } 35 | 36 | size_t readBytes(Stream &target, char *buffer, size_t size) { 37 | size_t result = target.readBytes(buffer, size); 38 | _log.print("readBytes("); 39 | _log.print(size); 40 | _log.print(") -> "); 41 | _log.print(result); 42 | if (size > result) 43 | _log.print(" [timeout]"); 44 | _log.println(); 45 | return result; 46 | } 47 | 48 | int read(Client &target, uint8_t *buffer, size_t size) { 49 | int result = target.read(buffer, size); 50 | _log.print("read("); 51 | _log.print(size); 52 | _log.print(") -> "); 53 | _log.print(result); 54 | if (static_cast(size) > result) 55 | _log.print(" [timeout]"); 56 | _log.println(); 57 | return result; 58 | } 59 | 60 | private: 61 | Print &_log; 62 | }; 63 | 64 | } // namespace StreamUtils 65 | -------------------------------------------------------------------------------- /src/StreamUtils/Streams/ProgmemStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Configuration.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | class ProgmemStream : public Stream { 14 | public: 15 | ProgmemStream(const void* ptr, size_t size) 16 | : _ptr(reinterpret_cast(ptr)), _size(size) {} 17 | 18 | ProgmemStream(const char* ptr) : _ptr(ptr), _size(ptr ? strlen_P(ptr) : 0) {} 19 | 20 | ProgmemStream(const __FlashStringHelper* ptr) 21 | : ProgmemStream{reinterpret_cast(ptr)} {} 22 | 23 | int available() override { 24 | return _size; 25 | } 26 | 27 | int read() override { 28 | if (_size <= 0) 29 | return -1; 30 | _size--; 31 | return pgm_read_byte(_ptr++); 32 | } 33 | 34 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 35 | size_t readBytes(char* buffer, size_t size) override { 36 | if (size > _size) 37 | size = _size; 38 | memcpy_P(buffer, _ptr, size); 39 | _ptr += size; 40 | _size -= size; 41 | return size; 42 | } 43 | #endif 44 | 45 | int peek() override { 46 | if (_size <= 0) 47 | return -1; 48 | return pgm_read_byte(_ptr); 49 | } 50 | 51 | void flush() override {} 52 | 53 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 54 | size_t write(const void*, uint32) override { 55 | return 0; 56 | } 57 | #else 58 | size_t write(const uint8_t*, size_t) override { 59 | return 0; 60 | } 61 | #endif 62 | 63 | size_t write(uint8_t) override { 64 | return 0; 65 | } 66 | 67 | private: 68 | const char* _ptr; 69 | size_t _size; 70 | }; 71 | 72 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Streams/MemoryStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Buffers/CircularBuffer.hpp" 10 | #include "../Configuration.hpp" 11 | #include "../Ports/DefaultAllocator.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | class BasicMemoryStream : public Stream { 17 | public: 18 | BasicMemoryStream(size_t capacity, TAllocator allocator = TAllocator()) 19 | : _buffer(capacity, allocator) {} 20 | 21 | BasicMemoryStream(const BasicMemoryStream &src) : _buffer(src._buffer) {} 22 | 23 | int available() override { 24 | return static_cast(_buffer.available()); 25 | } 26 | 27 | int peek() override { 28 | return _buffer.isEmpty() ? -1 : _buffer.peek(); 29 | } 30 | 31 | int read() override { 32 | return _buffer.isEmpty() ? -1 : _buffer.read(); 33 | } 34 | 35 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 36 | size_t readBytes(char *data, size_t size) override { 37 | return _buffer.readBytes(data, size); 38 | } 39 | #endif 40 | 41 | size_t write(uint8_t data) override { 42 | return _buffer.isFull() ? 0 : _buffer.write(data); 43 | } 44 | 45 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 46 | size_t write(const void *p, uint32 size) override { 47 | const uint8_t *data = reinterpret_cast(p); 48 | #else 49 | size_t write(const uint8_t *data, size_t size) override { 50 | #endif 51 | return _buffer.write(data, size); 52 | } 53 | 54 | using Stream::write; 55 | 56 | void flush() override { 57 | _buffer.clear(); 58 | } 59 | 60 | private: 61 | CircularBuffer _buffer; 62 | }; 63 | 64 | using MemoryStream = BasicMemoryStream; 65 | 66 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/images/HammingDecodingStream.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginalparity bits -------------------------------------------------------------------------------- /src/StreamUtils/Policies/WriteBufferingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Buffers/LinearBuffer.hpp" 10 | #include "../Configuration.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | struct WriteBufferingPolicy { 16 | public: 17 | WriteBufferingPolicy(size_t capacity, TAllocator allocator) 18 | : _buffer(capacity, allocator) {} 19 | 20 | size_t write(Print &target, const uint8_t *data, size_t size) { 21 | size_t result = 0; 22 | 23 | // continue to fill the buffer? 24 | if (!_buffer.isEmpty()) { 25 | size_t n = _buffer.write(data, size); 26 | data += n; 27 | size -= n; 28 | result += n; 29 | 30 | // time to flush? 31 | if (_buffer.isFull()) { 32 | _buffer.flushInto(target); 33 | } 34 | } 35 | 36 | // something left to write? 37 | if (size > 0) { 38 | // can we bypass the buffer? 39 | if (size >= _buffer.capacity()) { 40 | result += target.write(data, size); 41 | } else { 42 | result += _buffer.write(data, size); 43 | } 44 | } 45 | return result; 46 | } 47 | 48 | size_t write(Print &target, uint8_t data) { 49 | if (!_buffer) 50 | return target.write(data); 51 | 52 | _buffer.write(data); 53 | if (_buffer.isFull()) 54 | _buffer.flushInto(target); 55 | return 1; 56 | } 57 | 58 | void flush(Stream &target) { 59 | _buffer.flushInto(target); 60 | target.flush(); 61 | } 62 | 63 | void flush(Print &target) { 64 | _buffer.flushInto(target); 65 | #if STREAMUTILS_PRINT_FLUSH_EXISTS 66 | target.flush(); 67 | #endif 68 | } 69 | 70 | void implicitFlush(Print &target) { 71 | _buffer.flushInto(target); 72 | } 73 | 74 | private: 75 | LinearBuffer _buffer; 76 | }; 77 | 78 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/images/ReadBuffer.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginalbuffer -------------------------------------------------------------------------------- /extras/images/WriteBuffer.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginalbuffer -------------------------------------------------------------------------------- /src/StreamUtils/Streams/StreamProxy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "../Polyfills.hpp" 9 | 10 | namespace StreamUtils { 11 | 12 | template 13 | class StreamProxy : public Stream { 14 | public: 15 | explicit StreamProxy(Stream &upstream, ReadPolicy reader = ReadPolicy{}, 16 | WritePolicy writer = WritePolicy{}) 17 | : _upstream(upstream), 18 | _reader(reader), 19 | _writer(Polyfills::move(writer)) {} 20 | 21 | StreamProxy(const StreamProxy &other) 22 | : _upstream(other._upstream), 23 | _reader(other._reader), 24 | _writer(other._writer) {} 25 | 26 | ~StreamProxy() { 27 | _writer.implicitFlush(_upstream); 28 | } 29 | 30 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 31 | size_t write(const void *data, uint32 size) override { 32 | const uint8_t *buffer = reinterpret_cast(data); 33 | #else 34 | size_t write(const uint8_t *buffer, size_t size) override { 35 | #endif 36 | return _writer.write(_upstream, buffer, size); 37 | } 38 | 39 | size_t write(uint8_t data) override { 40 | return _writer.write(_upstream, data); 41 | } 42 | 43 | using Stream::write; 44 | 45 | int available() override { 46 | return _reader.available(_upstream); 47 | } 48 | 49 | int read() override { 50 | return _reader.read(_upstream); 51 | } 52 | 53 | int peek() override { 54 | return _reader.peek(_upstream); 55 | } 56 | 57 | void flush() override { 58 | _writer.flush(_upstream); 59 | } 60 | 61 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 62 | size_t readBytes(char *buffer, size_t size) override { 63 | return _reader.readBytes(_upstream, buffer, size); 64 | } 65 | #endif 66 | 67 | protected: 68 | Stream &_upstream; 69 | ReadPolicy _reader; 70 | WritePolicy _writer; 71 | }; 72 | 73 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Streams/EepromStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include "../Configuration.hpp" 8 | 9 | #if STREAMUTILS_ENABLE_EEPROM 10 | 11 | #include 12 | #include 13 | 14 | namespace StreamUtils { 15 | 16 | class EepromStream : public Stream { 17 | public: 18 | EepromStream(size_t address, size_t size) 19 | : _readAddress(address), _writeAddress(address), _end(address + size) {} 20 | 21 | int available() override { 22 | return static_cast(_end - _readAddress); 23 | } 24 | 25 | int read() override { 26 | if (_readAddress >= _end) 27 | return -1; 28 | return EEPROM.read(static_cast(_readAddress++)); 29 | } 30 | 31 | int peek() override { 32 | if (_readAddress >= _end) 33 | return -1; 34 | return EEPROM.read(static_cast(_readAddress)); 35 | } 36 | 37 | void flush() override { 38 | #if STREAMUTILS_USE_EEPROM_COMMIT 39 | EEPROM.commit(); 40 | #endif 41 | } 42 | 43 | using Print::write; 44 | 45 | size_t write(const uint8_t *buffer, size_t size) override { 46 | size_t remaining = _end - _writeAddress; 47 | if (size > remaining) 48 | size = remaining; 49 | for (size_t i = 0; i < size; i++) { 50 | int address = static_cast(_writeAddress++); 51 | #if STREAMUTILS_USE_EEPROM_UPDATE 52 | EEPROM.update(address, buffer[i]); 53 | #else 54 | EEPROM.write(address, buffer[i]); 55 | #endif 56 | } 57 | return size; 58 | } 59 | 60 | size_t write(uint8_t data) override { 61 | if (_writeAddress >= _end) 62 | return 0; 63 | int address = static_cast(_writeAddress++); 64 | #if STREAMUTILS_USE_EEPROM_UPDATE 65 | EEPROM.update(address, data); 66 | #else 67 | EEPROM.write(address, data); 68 | #endif 69 | return 1; 70 | } 71 | 72 | private: 73 | size_t _readAddress, _writeAddress, _end; 74 | }; 75 | 76 | } // namespace StreamUtils 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /extras/images/ChunkDecodingStream.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginal -------------------------------------------------------------------------------- /src/StreamUtils/Streams/StringStream.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "../Configuration.hpp" 11 | #include "../Polyfills.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | class StringStream : public Stream { 16 | public: 17 | StringStream() {} 18 | 19 | explicit StringStream(String str) : _str(Polyfills::move(str)) {} 20 | 21 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 22 | size_t write(const void* data, uint32 n) override { 23 | const uint8_t* p = reinterpret_cast(data); 24 | #else 25 | size_t write(const uint8_t* p, size_t n) override { 26 | #endif 27 | for (size_t i = 0; i < n; i++) { 28 | uint8_t c = p[i]; 29 | if (c == 0) 30 | return i; 31 | write(c); 32 | } 33 | return n; 34 | } 35 | 36 | size_t write(uint8_t c) override { 37 | if (c == 0) 38 | return 0; 39 | _str += static_cast(c); 40 | return 1; 41 | } 42 | 43 | const String& str() const { 44 | return _str; 45 | } 46 | 47 | void str(String str) { 48 | _str = Polyfills::move(str); 49 | } 50 | 51 | int available() override { 52 | return static_cast(_str.length()); 53 | } 54 | 55 | int read() override { 56 | if (_str.length() == 0) 57 | return -1; 58 | char c = _str[0]; 59 | _str.remove(0, 1); 60 | return c; 61 | } 62 | 63 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 64 | size_t readBytes(char* buffer, size_t length) override { 65 | if (length > _str.length()) 66 | length = _str.length(); 67 | // Don't use _str.ToCharArray() because it inserts a terminator 68 | memcpy(buffer, _str.c_str(), length); 69 | _str.remove(0, static_cast(length)); 70 | return length; 71 | } 72 | #endif 73 | 74 | int peek() override { 75 | return _str.length() > 0 ? _str[0] : -1; 76 | } 77 | 78 | void flush() override {} 79 | 80 | private: 81 | String _str; 82 | }; 83 | } // namespace StreamUtils -------------------------------------------------------------------------------- /examples/ChunkDecoding/ChunkDecoding.ino: -------------------------------------------------------------------------------- 1 | #if defined(ARDUINO_ARCH_ESP8266) 2 | #include 3 | #include 4 | #elif defined(ARDUINO_ARCH_ESP32) 5 | #include 6 | #include 7 | #include 8 | #else 9 | #error Unsuported platform 10 | #endif 11 | 12 | #include 13 | 14 | // WiFi network configuration. 15 | const char* WIFI_SSID = "*****EDIT*****"; 16 | const char* WIFI_PASSWORD = "*****EDIT*****"; 17 | 18 | void setup() { 19 | // Initialize Serial Port 20 | Serial.begin(115200); 21 | while (!Serial) 22 | continue; 23 | 24 | // Connect to the WLAN 25 | WiFi.mode(WIFI_STA); 26 | WiFi.begin(WIFI_SSID, WIFI_PASSWORD); 27 | while (WiFi.status() != WL_CONNECTED) { 28 | Serial.println(F("Connecting to Wifi...")); 29 | delay(500); 30 | } 31 | 32 | // Initialize the SSL library 33 | WiFiClientSecure client; 34 | client.setInsecure(); // ignore server's certificate 35 | 36 | // Send the request 37 | HTTPClient http; 38 | http.begin(client, F("https://jigsaw.w3.org/HTTP/ChunkedScript")); 39 | 40 | // Ask HTTPClient to collect the Transfer-Encoding header 41 | // (by default it discards all headers) 42 | const char* keys[] = {"Transfer-Encoding"}; 43 | http.collectHeaders(keys, 1); 44 | 45 | Serial.println(F("Sending request...")); 46 | int status = http.GET(); 47 | if (status != 200) { 48 | Serial.print(F("Unexpected HTTP status: ")); 49 | Serial.println(status); 50 | return; 51 | } 52 | 53 | // Get a reference to the stream 54 | Stream& rawStream = http.getStream(); 55 | ChunkDecodingStream decodedStream(http.getStream()); 56 | Stream& response = 57 | http.header("Transfer-Encoding") == "chunked" ? decodedStream : rawStream; 58 | 59 | // Read and print the response 60 | char buffer[256]; 61 | size_t n = 0; 62 | do { 63 | n = response.readBytes(buffer, sizeof(buffer)); 64 | Serial.write(buffer, n); 65 | } while (n == sizeof(buffer)); 66 | 67 | // Disconnect 68 | http.end(); 69 | 70 | Serial.println(F("Done!")); 71 | } 72 | 73 | void loop() {} 74 | -------------------------------------------------------------------------------- /examples/StringPrint/StringPrint.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to use StringPrint 6 | 7 | #include 8 | 9 | void setup() { 10 | // Initialize serial port 11 | Serial.begin(9600); 12 | while (!Serial) 13 | continue; 14 | 15 | StringPrint stream; 16 | 17 | stream.print("Temperature = "); 18 | stream.print(22.3); 19 | stream.print(" °C"); 20 | 21 | Serial.print(stream.str()); 22 | } 23 | 24 | void loop() { 25 | // no used in this example 26 | } 27 | 28 | /***************************************************** 29 | * * 30 | * Love this project? * 31 | * Star it on GitHub! * 32 | * * 33 | * .,,. * 34 | * ,,:1. * 35 | * ,.,:;1 * 36 | * .,,,::;: * 37 | * ,,,,::;;. * 38 | * .,,,:::;;; * 39 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 40 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 41 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 42 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 43 | * :;;:,,,,,,::::::;;;;;;l1 * 44 | * ,,,,:::::::;;;;;;l * 45 | * .,,,,::::;;;;;;;:::: * 46 | * ,,,,,:::;;;;;::,:::1 * 47 | * ,,,,,::;;;t1:,,:::::;l * 48 | * .,,,,:;;ll ;::::::;;, * 49 | * ,,,:;ll. .1:::;;l * 50 | * .,:lt, .1;;l: * 51 | * * 52 | * https://github.com/bblanchon/ArduinoStreamUtils * 53 | * * 54 | *****************************************************/ -------------------------------------------------------------------------------- /examples/ProgmemStream/ProgmemStream.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to use StringStream 6 | 7 | #include 8 | 9 | const char data[] PROGMEM = "This string is in program memory."; 10 | 11 | void setup() { 12 | // Initialize serial port 13 | Serial.begin(9600); 14 | while (!Serial) 15 | continue; 16 | 17 | ProgmemStream stream{data}; 18 | while (stream.available()) { 19 | Serial.write(stream.read()); 20 | } 21 | } 22 | 23 | void loop() { 24 | // not used in this example 25 | } 26 | 27 | /***************************************************** 28 | * * 29 | * Love this project? * 30 | * Star it on GitHub! * 31 | * * 32 | * .,,. * 33 | * ,,:1. * 34 | * ,.,:;1 * 35 | * .,,,::;: * 36 | * ,,,,::;;. * 37 | * .,,,:::;;; * 38 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 39 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 40 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 41 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 42 | * :;;:,,,,,,::::::;;;;;;l1 * 43 | * ,,,,:::::::;;;;;;l * 44 | * .,,,,::::;;;;;;;:::: * 45 | * ,,,,,:::;;;;;::,:::1 * 46 | * ,,,,,::;;;t1:,,:::::;l * 47 | * .,,,,:;;ll ;::::::;;, * 48 | * ,,,:;ll. .1:::;;l * 49 | * .,:lt, .1;;l: * 50 | * * 51 | * https://github.com/bblanchon/ArduinoStreamUtils * 52 | * * 53 | *****************************************************/ -------------------------------------------------------------------------------- /examples/StringStream/StringStream.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to use StringStream 6 | 7 | #include 8 | 9 | StringStream stream; 10 | 11 | void setup() { 12 | // Initialize serial port 13 | Serial.begin(9600); 14 | while (!Serial) 15 | continue; 16 | 17 | stream.print("Temperature = "); 18 | stream.print(22.3); 19 | stream.print(" °C"); 20 | } 21 | 22 | void loop() { 23 | if (stream.available() > 0) { 24 | Serial.print((char)stream.read()); 25 | } 26 | delay(250); 27 | } 28 | 29 | /***************************************************** 30 | * * 31 | * Love this project? * 32 | * Star it on GitHub! * 33 | * * 34 | * .,,. * 35 | * ,,:1. * 36 | * ,.,:;1 * 37 | * .,,,::;: * 38 | * ,,,,::;;. * 39 | * .,,,:::;;; * 40 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 41 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 42 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 43 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 44 | * :;;:,,,,,,::::::;;;;;;l1 * 45 | * ,,,,:::::::;;;;;;l * 46 | * .,,,,::::;;;;;;;:::: * 47 | * ,,,,,:::;;;;;::,:::1 * 48 | * ,,,,,::;;;t1:,,:::::;l * 49 | * .,,,,:;;ll ;::::::;;, * 50 | * ,,,:;ll. .1:::;;l * 51 | * .,:lt, .1;;l: * 52 | * * 53 | * https://github.com/bblanchon/ArduinoStreamUtils * 54 | * * 55 | *****************************************************/ -------------------------------------------------------------------------------- /extras/test/StringPrintTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Prints/StringPrint.hpp" 6 | 7 | #include "doctest.h" 8 | 9 | using namespace StreamUtils; 10 | 11 | TEST_CASE("StringPrint") { 12 | WHEN("Constructed with no argument") { 13 | StringPrint s; 14 | 15 | THEN("str() return an empty String") { 16 | CHECK(s.str() == ""); 17 | } 18 | 19 | THEN("write(uint8_t) appends the string") { 20 | s.write('A'); 21 | s.write('B'); 22 | CHECK(s.str() == "AB"); 23 | } 24 | 25 | THEN("write(uint8_t) return 1") { 26 | CHECK(s.write('A') == 1); 27 | CHECK(s.write('B') == 1); 28 | } 29 | 30 | THEN("write(0) return 0") { 31 | CHECK(s.write(0) == 0); 32 | } 33 | 34 | THEN("write(uint8_t*, size_t) appends the string") { 35 | s.write(reinterpret_cast("ABXXX"), 2); 36 | s.write(reinterpret_cast("CDEXX"), 3); 37 | CHECK(s.str() == "ABCDE"); 38 | } 39 | 40 | THEN("write(uint8_t*, size_t) return number of chars written") { 41 | uint8_t dummy[32] = {1, 2, 3, 0, 5, 6}; 42 | CHECK(s.write(dummy, 2) == 2); 43 | CHECK(s.write(dummy, 3) == 3); 44 | CHECK(s.write(dummy, 4) == 3); 45 | } 46 | 47 | THEN("str(String) sets the string") { 48 | s.str("world!"); 49 | CHECK(s.str() == "world!"); 50 | } 51 | } 52 | 53 | WHEN("Constructed with string") { 54 | StringPrint s("hello"); 55 | 56 | THEN("str() return the stirng passed to contructor") { 57 | CHECK(s.str() == "hello"); 58 | } 59 | 60 | THEN("write(uint8_t) appends the string") { 61 | s.write('A'); 62 | s.write('B'); 63 | CHECK(s.str() == "helloAB"); 64 | } 65 | 66 | THEN("write(uint8_t*, size_t) appends the string") { 67 | s.write(reinterpret_cast("ABXXX"), 2); 68 | s.write(reinterpret_cast("CDEXX"), 3); 69 | CHECK(s.str() == "helloABCDE"); 70 | } 71 | 72 | THEN("str(String) replaces the string") { 73 | s.str("world!"); 74 | CHECK(s.str() == "world!"); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /extras/images/HammingEncodingStream.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginalparity bits -------------------------------------------------------------------------------- /extras/images/ReadLogger.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginallog -------------------------------------------------------------------------------- /extras/images/WriteLogger.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginallog -------------------------------------------------------------------------------- /extras/test/ReadLoggingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "FailingAllocator.hpp" 6 | 7 | #include "StreamUtils/Prints/StringPrint.hpp" 8 | #include "StreamUtils/Streams/MemoryStream.hpp" 9 | #include "StreamUtils/Streams/ReadLoggingStream.hpp" 10 | #include "StreamUtils/Streams/SpyingStream.hpp" 11 | 12 | #include "doctest.h" 13 | 14 | using namespace StreamUtils; 15 | 16 | TEST_CASE("ReadLoggingStream") { 17 | MemoryStream upstream(4); 18 | 19 | StringPrint log; 20 | SpyingStream upstreamSpy{upstream, log}; 21 | 22 | StringPrint output; 23 | ReadLoggingStream loggingStream{upstreamSpy, output}; 24 | 25 | SUBCASE("available()") { 26 | upstream.print("ABC"); 27 | 28 | size_t n = loggingStream.available(); 29 | 30 | CHECK(n == 3); 31 | CHECK(log.str() == "available() -> 3"); 32 | CHECK(output.str() == ""); 33 | } 34 | 35 | SUBCASE("peek()") { 36 | upstream.print("ABC"); 37 | 38 | int n = loggingStream.peek(); 39 | 40 | CHECK(n == 'A'); 41 | CHECK(log.str() == "peek() -> 65"); 42 | CHECK(output.str() == ""); 43 | } 44 | 45 | SUBCASE("read()") { 46 | upstream.print("ABC"); 47 | 48 | int n = loggingStream.read(); 49 | 50 | CHECK(n == 'A'); 51 | CHECK(log.str() == "read() -> 65"); 52 | CHECK(output.str() == "A"); 53 | } 54 | 55 | SUBCASE("readBytes()") { 56 | upstream.print("ABC"); 57 | 58 | char s[4] = {0}; 59 | size_t n = loggingStream.readBytes(s, 4); 60 | 61 | CHECK(n == 3); 62 | CHECK(output.str() == "ABC"); 63 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 64 | CHECK(log.str() == "readBytes(4) -> 3 [timeout]"); 65 | #endif 66 | } 67 | 68 | SUBCASE("write(char)") { 69 | size_t n = loggingStream.write('A'); 70 | 71 | CHECK(n == 1); 72 | CHECK(log.str() == "write('A') -> 1"); 73 | CHECK(output.str() == ""); 74 | } 75 | 76 | SUBCASE("write(char*,size_t)") { 77 | size_t n = loggingStream.write("ABCDEF", 6); 78 | 79 | CHECK(n == 4); 80 | CHECK(log.str() == "write('ABCDEF', 6) -> 4"); 81 | CHECK(output.str() == ""); 82 | } 83 | 84 | SUBCASE("flush()") { 85 | loggingStream.flush(); 86 | 87 | CHECK(log.str() == "flush()"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /extras/test/ReadThrottlingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "FailingAllocator.hpp" 6 | 7 | #include "StreamUtils/Streams/ReadThrottlingStream.hpp" 8 | #include "StreamUtils/Streams/StringStream.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | #include 13 | 14 | using namespace StreamUtils; 15 | 16 | class SpyingThrottler { 17 | public: 18 | SpyingThrottler(uint32_t rate) { 19 | _log << "C(" << rate << ")"; 20 | } 21 | 22 | SpyingThrottler(const SpyingThrottler& src) { 23 | _log << src._log.str(); 24 | } 25 | 26 | void throttle() { 27 | _log << "T"; 28 | } 29 | 30 | std::string log() const { 31 | return _log.str(); 32 | } 33 | 34 | private: 35 | std::stringstream _log; 36 | }; 37 | 38 | using ReadThrottlingStream = BasicReadThrottlingStream; 39 | 40 | TEST_CASE("ReadThrottlingStream") { 41 | StringStream upstream; 42 | 43 | ReadThrottlingStream throttledStream{upstream, 4}; 44 | Stream& stream = throttledStream; 45 | const SpyingThrottler& throttler = throttledStream.throttler(); 46 | 47 | SUBCASE("available()") { 48 | upstream.print("ABCD"); 49 | CHECK(stream.available() == 4); 50 | CHECK(throttler.log() == "C(4)"); 51 | } 52 | 53 | SUBCASE("read()") { 54 | upstream.print("ABCD"); 55 | int n = stream.read(); 56 | 57 | CHECK(n == 'A'); 58 | CHECK(throttler.log() == "C(4)T"); 59 | } 60 | 61 | SUBCASE("readBytes()") { 62 | upstream.print("ABCD"); 63 | char output[8] = {0}; 64 | 65 | SUBCASE("read more than available") { 66 | size_t n = stream.readBytes(output, sizeof(output)); 67 | 68 | CHECK(n == 4); 69 | CHECK(strcmp("ABCD", output) == 0); 70 | CHECK(throttler.log() == "C(4)TTTTT"); 71 | } 72 | 73 | SUBCASE("read less than available") { 74 | size_t n = stream.readBytes(output, 2); 75 | 76 | CHECK(n == 2); 77 | CHECK(strcmp("AB", output) == 0); 78 | CHECK(throttler.log() == "C(4)TT"); 79 | } 80 | 81 | SUBCASE("read as many as available") { 82 | size_t n = stream.readBytes(output, 4); 83 | 84 | CHECK(n == 4); 85 | CHECK(strcmp("ABCD", output) == 0); 86 | CHECK(throttler.log() == "C(4)TTTT"); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /extras/test/WriteLoggingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "FailingAllocator.hpp" 6 | 7 | #include "StreamUtils/Prints/StringPrint.hpp" 8 | #include "StreamUtils/Streams/MemoryStream.hpp" 9 | #include "StreamUtils/Streams/SpyingStream.hpp" 10 | #include "StreamUtils/Streams/WriteLoggingStream.hpp" 11 | 12 | #include "doctest.h" 13 | 14 | using namespace StreamUtils; 15 | 16 | TEST_CASE("WriteLoggingStream") { 17 | MemoryStream upstream(4); 18 | 19 | StringPrint log; 20 | SpyingStream upstreamSpy{upstream, log}; 21 | 22 | StringPrint output; 23 | WriteLoggingStream loggingStream{upstreamSpy, output}; 24 | 25 | SUBCASE("available()") { 26 | upstream.print("ABC"); 27 | 28 | size_t n = loggingStream.available(); 29 | 30 | CHECK(n == 3); 31 | CHECK(log.str() == "available() -> 3"); 32 | CHECK(output.str() == ""); 33 | } 34 | 35 | SUBCASE("peek()") { 36 | upstream.print("ABC"); 37 | 38 | int n = loggingStream.peek(); 39 | 40 | CHECK(n == 'A'); 41 | CHECK(log.str() == "peek() -> 65"); 42 | CHECK(output.str() == ""); 43 | } 44 | 45 | SUBCASE("read()") { 46 | upstream.print("ABC"); 47 | 48 | int n = loggingStream.read(); 49 | 50 | CHECK(n == 'A'); 51 | CHECK(log.str() == "read() -> 65"); 52 | CHECK(output.str() == ""); 53 | } 54 | 55 | SUBCASE("readBytes()") { 56 | upstream.print("ABC"); 57 | 58 | char s[4] = {0}; 59 | size_t n = loggingStream.readBytes(s, 4); 60 | 61 | CHECK(n == 3); 62 | CHECK(output.str() == ""); 63 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 64 | CHECK(log.str() == "readBytes(4) -> 3 [timeout]"); 65 | #endif 66 | } 67 | 68 | SUBCASE("write(char)") { 69 | size_t n = loggingStream.write('A'); 70 | 71 | CHECK(n == 1); 72 | CHECK(log.str() == "write('A') -> 1"); 73 | CHECK(output.str() == "A"); 74 | } 75 | 76 | SUBCASE("write(char*,size_t)") { 77 | size_t n = loggingStream.write("ABCDEF", 6); 78 | 79 | CHECK(n == 4); 80 | CHECK(log.str() == "write('ABCDEF', 6) -> 4"); 81 | CHECK(output.str() == "ABCD"); 82 | } 83 | 84 | SUBCASE("flush()") { 85 | loggingStream.flush(); 86 | 87 | CHECK(log.str() == "flush()"); 88 | CHECK(output.str() == ""); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/StreamUtils/Configuration.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #ifndef STREAMUTILS_PRINT_FLUSH_EXISTS 8 | #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_SAMD) || \ 9 | defined(ARDUINO_ARCH_AVR) || \ 10 | (defined(ARDUINO_ARCH_ESP32) && ESP_ARDUINO_VERSION_MAJOR >= 2 && \ 11 | ESP_ARDUINO_VERSION_PATCH >= 3) || \ 12 | (defined(ARDUINO_ARCH_STM32) && STM32_CORE_VERSION_MAJOR >= 2) 13 | #define STREAMUTILS_PRINT_FLUSH_EXISTS 1 14 | #else 15 | #define STREAMUTILS_PRINT_FLUSH_EXISTS 0 16 | #endif 17 | #endif 18 | 19 | #ifndef STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 20 | #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) || \ 21 | defined(ARDUINO_ARCH_STM32) 22 | #define STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 1 23 | #else 24 | #define STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 0 25 | #endif 26 | #endif 27 | 28 | #ifndef STREAMUTILS_PRINT_WRITE_VOID_UINT32 29 | #if defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F4) 30 | #define STREAMUTILS_PRINT_WRITE_VOID_UINT32 1 31 | #else 32 | #define STREAMUTILS_PRINT_WRITE_VOID_UINT32 0 33 | #endif 34 | #endif 35 | 36 | #ifndef STREAMUTILS_ENABLE_EEPROM 37 | #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266) || \ 38 | defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_PICO_MAJOR) || \ 39 | defined(ARDUINO_ARCH_STM32) || defined(CORE_TEENSY) || \ 40 | defined(ARDUINO_ARCH_MEGAAVR) 41 | #define STREAMUTILS_ENABLE_EEPROM 1 42 | #else 43 | #define STREAMUTILS_ENABLE_EEPROM 0 44 | #endif 45 | #endif 46 | 47 | #ifndef STREAMUTILS_USE_EEPROM_COMMIT 48 | #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) || \ 49 | defined(ARDUINO_ARCH_RP2040) 50 | #define STREAMUTILS_USE_EEPROM_COMMIT 1 51 | #else 52 | #define STREAMUTILS_USE_EEPROM_COMMIT 0 53 | #endif 54 | #endif 55 | 56 | #ifndef STREAMUTILS_USE_EEPROM_UPDATE 57 | #if defined(ARDUINO_ARCH_AVR) || defined(CORE_TEENSY) || \ 58 | defined(ARDUINO_ARCH_MEGAAVR) 59 | #define STREAMUTILS_USE_EEPROM_UPDATE 1 60 | #else 61 | #define STREAMUTILS_USE_EEPROM_UPDATE 0 62 | #endif 63 | #endif 64 | 65 | #ifndef STREAMUTILS_STACK_BUFFER_MAX_SIZE 66 | #define STREAMUTILS_STACK_BUFFER_MAX_SIZE 32 67 | #endif 68 | -------------------------------------------------------------------------------- /examples/WriteLogger/WriteLogger.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to log what written to a Stream 6 | 7 | #include 8 | 9 | // Create a new stream that will forward all calls to Serial, and log to Serial. 10 | // Everything will be written twice to the Serial! 11 | WriteLoggingStream loggingStream(Serial, Serial); 12 | 13 | void setup() { 14 | // Initialize serial port 15 | Serial.begin(9600); 16 | while (!Serial) 17 | continue; 18 | 19 | // Write to the serial port. 20 | // Because loggingStream logs each write operation, the string will we written 21 | // twice 22 | loggingStream.println("Hello World!"); 23 | } 24 | 25 | void loop() { 26 | // not used in this example 27 | } 28 | 29 | /***************************************************** 30 | * * 31 | * Love this project? * 32 | * Star it on GitHub! * 33 | * * 34 | * .,,. * 35 | * ,,:1. * 36 | * ,.,:;1 * 37 | * .,,,::;: * 38 | * ,,,,::;;. * 39 | * .,,,:::;;; * 40 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 41 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 42 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 43 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 44 | * :;;:,,,,,,::::::;;;;;;l1 * 45 | * ,,,,:::::::;;;;;;l * 46 | * .,,,,::::;;;;;;;:::: * 47 | * ,,,,,:::;;;;;::,:::1 * 48 | * ,,,,,::;;;t1:,,:::::;l * 49 | * .,,,,:;;ll ;::::::;;, * 50 | * ,,,:;ll. .1:::;;l * 51 | * .,:lt, .1;;l: * 52 | * * 53 | * https://github.com/bblanchon/ArduinoStreamUtils * 54 | * * 55 | *****************************************************/ -------------------------------------------------------------------------------- /examples/ReadLogger/ReadLogger.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to log what read from a Stream 6 | 7 | #include 8 | 9 | // Create a new stream that will forward all calls to Serial, and log to Serial. 10 | // It will write back everything it reads, just like "echo" 11 | ReadLoggingStream loggingStream(Serial, Serial); 12 | 13 | void setup() { 14 | // Initialize serial port 15 | Serial.begin(9600); 16 | while (!Serial) 17 | continue; 18 | } 19 | 20 | void loop() { 21 | // Read the serial port. 22 | // Because loggingStream write everything it read, the program will show what 23 | // you sent. 24 | while (Serial.available()) { 25 | loggingStream.write(Serial.read()); 26 | } 27 | } 28 | 29 | /***************************************************** 30 | * * 31 | * Love this project? * 32 | * Star it on GitHub! * 33 | * * 34 | * .,,. * 35 | * ,,:1. * 36 | * ,.,:;1 * 37 | * .,,,::;: * 38 | * ,,,,::;;. * 39 | * .,,,:::;;; * 40 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 41 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 42 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 43 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 44 | * :;;:,,,,,,::::::;;;;;;l1 * 45 | * ,,,,:::::::;;;;;;l * 46 | * .,,,,::::;;;;;;;:::: * 47 | * ,,,,,:::;;;;;::,:::1 * 48 | * ,,,,,::;;;t1:,,:::::;l * 49 | * .,,,,:;;ll ;::::::;;, * 50 | * ,,,:;ll. .1:::;;l * 51 | * .,:lt, .1;;l: * 52 | * * 53 | * https://github.com/bblanchon/ArduinoStreamUtils * 54 | * * 55 | *****************************************************/ -------------------------------------------------------------------------------- /extras/test/LoggingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Prints/StringPrint.hpp" 6 | #include "StreamUtils/Streams/LoggingStream.hpp" 7 | #include "StreamUtils/Streams/MemoryStream.hpp" 8 | #include "StreamUtils/Streams/SpyingStream.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | using namespace StreamUtils; 13 | 14 | TEST_CASE("LoggingStream") { 15 | MemoryStream upstream{4}; 16 | StringPrint output; 17 | 18 | StringPrint log; 19 | SpyingStream upstreamSpy{upstream, log}; 20 | 21 | LoggingStream loggingStream{upstreamSpy, output}; 22 | 23 | // upstream -> upstreamSpy -> loggingStream -> output 24 | // | 25 | // v 26 | // log 27 | 28 | SUBCASE("available()") { 29 | upstream.print("ABC"); 30 | 31 | size_t n = loggingStream.available(); 32 | 33 | CHECK(n == 3); 34 | CHECK(log.str() == "available() -> 3"); 35 | CHECK(output.str() == ""); 36 | } 37 | 38 | SUBCASE("peek()") { 39 | upstream.print("ABC"); 40 | 41 | int n = loggingStream.peek(); 42 | 43 | CHECK(n == 'A'); 44 | CHECK(log.str() == "peek() -> 65"); 45 | CHECK(output.str() == ""); 46 | } 47 | 48 | SUBCASE("read()") { 49 | upstream.print("ABC"); 50 | 51 | int n = loggingStream.read(); 52 | 53 | CHECK(n == 'A'); 54 | CHECK(log.str() == "read() -> 65"); 55 | CHECK(output.str() == "A"); 56 | } 57 | 58 | SUBCASE("readBytes()") { 59 | upstream.print("ABC"); 60 | 61 | char s[4] = {0}; 62 | size_t n = loggingStream.readBytes(s, 4); 63 | 64 | CHECK(n == 3); 65 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 66 | CHECK(log.str() == "readBytes(4) -> 3 [timeout]"); 67 | #endif 68 | CHECK(output.str() == "ABC"); 69 | } 70 | 71 | SUBCASE("write(char)") { 72 | size_t n = loggingStream.write('A'); 73 | 74 | CHECK(n == 1); 75 | CHECK(log.str() == "write('A') -> 1"); 76 | CHECK(output.str() == "A"); 77 | } 78 | 79 | SUBCASE("write(char*,size_t)") { 80 | size_t n = loggingStream.write("ABCDEF", 6); 81 | 82 | CHECK(n == 4); 83 | CHECK(log.str() == "write('ABCDEF', 6) -> 4"); 84 | CHECK(output.str() == "ABCD"); 85 | } 86 | 87 | SUBCASE("flush()") { 88 | loggingStream.flush(); 89 | 90 | CHECK(log.str() == "flush()"); 91 | CHECK(output.str() == ""); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/StreamUtils/Buffers/CircularBuffer.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "CharArray.hpp" 10 | 11 | namespace StreamUtils { 12 | 13 | template 14 | class CircularBuffer { 15 | public: 16 | CircularBuffer(size_t capacity, TAllocator allocator = TAllocator()) 17 | : _data(capacity, allocator) { 18 | _begin = _end = _size = 0; 19 | } 20 | 21 | CircularBuffer(const CircularBuffer &src) 22 | : CircularBuffer(src._data.size(), src._allocator) { 23 | if (_data) { 24 | _begin = src._begin; 25 | _end = src._end; 26 | _size = src._size; 27 | } 28 | } 29 | 30 | size_t available() const { 31 | return _size; 32 | } 33 | 34 | size_t capacity() const { 35 | return _data.size(); 36 | } 37 | 38 | void clear() { 39 | _begin = _end = _size = 0; 40 | } 41 | 42 | bool isEmpty() const { 43 | return _size == 0; 44 | } 45 | 46 | bool isFull() const { 47 | return _size == _data.size(); 48 | } 49 | 50 | char peek() const { 51 | assert(_size > 0); 52 | return _data[_begin]; 53 | } 54 | 55 | char read() { 56 | assert(_size > 0); 57 | char result = _data[_begin]; 58 | _begin = (_begin + 1) % _data.size(); 59 | _size--; 60 | return result; 61 | } 62 | 63 | size_t readBytes(char *data, size_t size) { 64 | // don't read more that available 65 | if (size > _size) 66 | size = _size; 67 | 68 | for (size_t i = 0; i < size; i++) 69 | data[i] = read(); 70 | 71 | return size; 72 | } 73 | 74 | size_t write(uint8_t data) { 75 | assert(_size < _data.size()); 76 | _data[_end] = data; 77 | _end = (_end + 1) % _data.size(); 78 | _size++; 79 | return 1; 80 | } 81 | 82 | size_t write(const uint8_t *data, size_t size) { 83 | // don't read more that available 84 | size_t roomLeft = _data.size() - _size; 85 | if (size > roomLeft) 86 | size = roomLeft; 87 | 88 | for (size_t i = 0; i < size; i++) 89 | write(data[i]); 90 | 91 | return size; 92 | } 93 | 94 | private: 95 | CharArray _data; 96 | size_t _size; 97 | size_t _begin; 98 | size_t _end; 99 | }; 100 | 101 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/ChunkDecodingClientTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Clients/ChunkDecodingClient.hpp" 6 | #include "StreamUtils/Clients/MemoryClient.hpp" 7 | #include "StreamUtils/Clients/SpyingClient.hpp" 8 | #include "StreamUtils/Prints/StringPrint.hpp" 9 | 10 | #include "doctest.h" 11 | 12 | using namespace StreamUtils; 13 | 14 | TEST_CASE("ChunkDecodingClient") { 15 | MemoryClient upstream(128); 16 | StringPrint log; 17 | SpyingClient spy{upstream, log}; 18 | ChunkDecodingClient client{spy}; 19 | 20 | // Most of the tests are in ChunkDecodingStreamTest.cpp 21 | // This file only tests the client-specific methods 22 | 23 | SUBCASE("read() merge chunks") { 24 | uint8_t buffer[32]; 25 | 26 | upstream.print( 27 | "4\r\n" 28 | "XXXX\r\n" 29 | "4\r\n" 30 | "YYYY\r\n" 31 | "0\r\n" 32 | "\r\n"); 33 | REQUIRE(client.read(buffer, 32) == 8); 34 | REQUIRE(buffer[0] == 'X'); 35 | REQUIRE(buffer[4] == 'Y'); 36 | } 37 | 38 | SUBCASE("read() waits timeout") { 39 | uint8_t buffer[32]; 40 | 41 | upstream.print( 42 | "4\r\n" 43 | "XXXX\r\n"); 44 | REQUIRE(client.read(buffer, 32) == 4); 45 | REQUIRE(buffer[0] == 'X'); 46 | 47 | REQUIRE(log.str() == 48 | "read(1) -> 1" // 1 49 | "read(1) -> 1" // \r 50 | "read(1) -> 1" // \n 51 | "read(4) -> 4" // XXXX 52 | "read(1) -> 1" // \r 53 | "read(1) -> 1" // \n 54 | "read(1) -> 0 [timeout]"); 55 | } 56 | 57 | SUBCASE("read() doen't wait if final chunk received") { 58 | uint8_t buffer[32]; 59 | 60 | upstream.print( 61 | "4\r\n" 62 | "XXXX\r\n" 63 | "0\r\n" 64 | "\r\n"); 65 | REQUIRE(client.read(buffer, 32) == 4); 66 | REQUIRE(buffer[0] == 'X'); 67 | REQUIRE(client.ended() == true); 68 | 69 | REQUIRE(log.str() == 70 | "read(1) -> 1" // 1 71 | "read(1) -> 1" // \r 72 | "read(1) -> 1" // \n 73 | "read(4) -> 4" // XXXX 74 | "read(1) -> 1" // \r 75 | "read(1) -> 1" // \n 76 | "read(1) -> 1" // 0 77 | "read(1) -> 1" // \r 78 | "read(1) -> 1" // \n 79 | "read(1) -> 1" // \r 80 | "read(1) -> 1" // \n 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/StreamUtils/Clients/MemoryClient.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Buffers/CircularBuffer.hpp" 10 | #include "../Configuration.hpp" 11 | #include "../Ports/DefaultAllocator.hpp" 12 | #include "../Streams/MemoryStream.hpp" 13 | 14 | namespace StreamUtils { 15 | 16 | template 17 | class BasicMemoryClient : public Client { 18 | public: 19 | BasicMemoryClient(size_t capacity, TAllocator allocator = TAllocator()) 20 | : _stream(capacity, allocator), _connected(false) {} 21 | 22 | BasicMemoryClient(const BasicMemoryClient &src) : _stream(src._stream) {} 23 | 24 | // --- Print --- 25 | 26 | size_t write(uint8_t data) override { 27 | return _stream.write(data); 28 | } 29 | 30 | size_t write(const uint8_t *data, size_t size) override { 31 | return _stream.write(data, size); 32 | } 33 | 34 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 35 | size_t write(const void *data, uint32 size) override { 36 | return _stream.write(data, size); 37 | } 38 | #endif 39 | 40 | // --- Stream --- 41 | 42 | int available() override { 43 | return _stream.available(); 44 | } 45 | 46 | int peek() override { 47 | return _stream.peek(); 48 | } 49 | 50 | int read() override { 51 | return _stream.read(); 52 | } 53 | 54 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 55 | size_t readBytes(char *data, size_t size) override { 56 | return _stream.readBytes(data, size); 57 | } 58 | #endif 59 | 60 | void flush() override { 61 | _stream.flush(); 62 | } 63 | 64 | // --- Client --- 65 | 66 | int connect(IPAddress, uint16_t) override { 67 | _connected = true; 68 | return 1; 69 | } 70 | 71 | int connect(const char *, uint16_t) override { 72 | _connected = true; 73 | return 1; 74 | } 75 | 76 | uint8_t connected() override { 77 | return _connected; 78 | } 79 | 80 | void stop() override { 81 | _connected = false; 82 | } 83 | 84 | operator bool() override { 85 | return true; 86 | } 87 | 88 | int read(uint8_t *buf, size_t size) override { 89 | return static_cast( 90 | _stream.readBytes(reinterpret_cast(buf), size)); 91 | } 92 | 93 | private: 94 | BasicMemoryStream _stream; 95 | bool _connected; 96 | }; 97 | using MemoryClient = BasicMemoryClient; 98 | 99 | } // namespace StreamUtils -------------------------------------------------------------------------------- /examples/EepromRead/EepromRead.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to read from EEPROM 6 | 7 | #include 8 | 9 | void setup() { 10 | // Initialize serial port 11 | Serial.begin(9600); 12 | while (!Serial) 13 | continue; 14 | 15 | #if STREAMUTILS_ENABLE_EEPROM 16 | 17 | #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) 18 | Serial.println("Initializing EEPROM..."); 19 | EEPROM.begin(512); 20 | #endif 21 | 22 | Serial.println("Make sure to run the EepromWrite example first!"); 23 | 24 | Serial.println("Reading EEPROM... "); 25 | EepromStream s(0, 12); 26 | Serial.print(s.readString()); 27 | 28 | #else 29 | Serial.println("EepromStream is not supported on this platform. Sorry"); 30 | #endif 31 | } 32 | 33 | void loop() { 34 | // not used in this example 35 | } 36 | 37 | /***************************************************** 38 | * * 39 | * Love this project? * 40 | * Star it on GitHub! * 41 | * * 42 | * .,,. * 43 | * ,,:1. * 44 | * ,.,:;1 * 45 | * .,,,::;: * 46 | * ,,,,::;;. * 47 | * .,,,:::;;; * 48 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 49 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 50 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 51 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 52 | * :;;:,,,,,,::::::;;;;;;l1 * 53 | * ,,,,:::::::;;;;;;l * 54 | * .,,,,::::;;;;;;;:::: * 55 | * ,,,,,:::;;;;;::,:::1 * 56 | * ,,,,,::;;;t1:,,:::::;l * 57 | * .,,,,:;;ll ;::::::;;, * 58 | * ,,,:;ll. .1:::;;l * 59 | * .,:lt, .1;;l: * 60 | * * 61 | * https://github.com/bblanchon/ArduinoStreamUtils * 62 | * * 63 | *****************************************************/ -------------------------------------------------------------------------------- /examples/ReadBuffer/ReadBuffer.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to buffer the read operations of a stream. 6 | // 7 | // Like "echo," it reads from the serial port and prints back the same thing. 8 | // What's interesting with this program is that it reads the input in chunks of 9 | // 64 bytes, even if it seems to read them one by one. 10 | 11 | #include 12 | 13 | ReadBufferingStream bufferedSerial{Serial, 64}; 14 | 15 | void setup() { 16 | // Initialize serial port 17 | Serial.begin(9600); 18 | while (!Serial) 19 | continue; 20 | } 21 | 22 | void loop() { 23 | // Even if it looks like the bytes are extracted one by one, they are actually 24 | // read by chunks of 64 bytes and placed in a buffer. 25 | while (bufferedSerial.available()) { 26 | Serial.write(bufferedSerial.read()); 27 | } 28 | } 29 | 30 | /***************************************************** 31 | * * 32 | * Love this project? * 33 | * Star it on GitHub! * 34 | * * 35 | * .,,. * 36 | * ,,:1. * 37 | * ,.,:;1 * 38 | * .,,,::;: * 39 | * ,,,,::;;. * 40 | * .,,,:::;;; * 41 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 42 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 43 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 44 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 45 | * :;;:,,,,,,::::::;;;;;;l1 * 46 | * ,,,,:::::::;;;;;;l * 47 | * .,,,,::::;;;;;;;:::: * 48 | * ,,,,,:::;;;;;::,:::1 * 49 | * ,,,,,::;;;t1:,,:::::;l * 50 | * .,,,,:;;ll ;::::::;;, * 51 | * ,,,:;ll. .1:::;;l * 52 | * .,:lt, .1;;l: * 53 | * * 54 | * https://github.com/bblanchon/ArduinoStreamUtils * 55 | * * 56 | *****************************************************/ -------------------------------------------------------------------------------- /examples/Logger/Logger.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to log what goes through a Stream, both reads and 6 | // writes operations. 7 | 8 | #include 9 | 10 | // Create a new stream that will forward all calls to Serial, and log to Serial. 11 | // Everything will be written twice to the Serial! 12 | LoggingStream loggingStream(Serial, Serial); 13 | 14 | void setup() { 15 | // Initialize serial port 16 | Serial.begin(9600); 17 | while (!Serial) 18 | continue; 19 | 20 | // Write to the serial port. 21 | // Because loggingStream logs each write operation, the string will we written 22 | // twice 23 | loggingStream.println("Hello World!"); 24 | } 25 | 26 | void loop() { 27 | // Read from the serial port. 28 | // Because loggingStream logs each read operation, everything we read is 29 | // printed back to the serial port. 30 | while (loggingStream.available()) { 31 | loggingStream.read(); 32 | } 33 | } 34 | 35 | /***************************************************** 36 | * * 37 | * Love this project? * 38 | * Star it on GitHub! * 39 | * * 40 | * .,,. * 41 | * ,,:1. * 42 | * ,.,:;1 * 43 | * .,,,::;: * 44 | * ,,,,::;;. * 45 | * .,,,:::;;; * 46 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 47 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 48 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 49 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 50 | * :;;:,,,,,,::::::;;;;;;l1 * 51 | * ,,,,:::::::;;;;;;l * 52 | * .,,,,::::;;;;;;;:::: * 53 | * ,,,,,:::;;;;;::,:::1 * 54 | * ,,,,,::;;;t1:,,:::::;l * 55 | * .,,,,:;;ll ;::::::;;, * 56 | * ,,,:;ll. .1:::;;l * 57 | * .,:lt, .1;;l: * 58 | * * 59 | * https://github.com/bblanchon/ArduinoStreamUtils * 60 | * * 61 | *****************************************************/ -------------------------------------------------------------------------------- /extras/images/HammingStream.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginalparity bitsparity bits -------------------------------------------------------------------------------- /examples/EepromWrite/EepromWrite.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to read from EEPROM 6 | 7 | #include 8 | 9 | void setup() { 10 | // Initialize serial port 11 | Serial.begin(9600); 12 | while (!Serial) 13 | continue; 14 | 15 | #if STREAMUTILS_ENABLE_EEPROM 16 | 17 | #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) 18 | Serial.println("Initializing EEPROM..."); 19 | EEPROM.begin(512); 20 | #endif 21 | 22 | Serial.println("Writing to EEPROM..."); 23 | EepromStream s(0, 12); 24 | s.print("Hello World!"); 25 | 26 | #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) 27 | Serial.println("Saving..."); 28 | s.flush(); // only required on ESP 29 | #endif 30 | 31 | Serial.println("Done!"); 32 | Serial.println("Now, run the EepromRead example."); 33 | 34 | #else 35 | Serial.println("EepromStream is not supported on this platform. Sorry"); 36 | #endif 37 | } 38 | 39 | void loop() { 40 | // not used in this example 41 | } 42 | 43 | /***************************************************** 44 | * * 45 | * Love this project? * 46 | * Star it on GitHub! * 47 | * * 48 | * .,,. * 49 | * ,,:1. * 50 | * ,.,:;1 * 51 | * .,,,::;: * 52 | * ,,,,::;;. * 53 | * .,,,:::;;; * 54 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 55 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 56 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 57 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 58 | * :;;:,,,,,,::::::;;;;;;l1 * 59 | * ,,,,:::::::;;;;;;l * 60 | * .,,,,::::;;;;;;;:::: * 61 | * ,,,,,:::;;;;;::,:::1 * 62 | * ,,,,,::;;;t1:,,:::::;l * 63 | * .,,,,:;;ll ;::::::;;, * 64 | * ,,,:;ll. .1:::;;l * 65 | * .,:lt, .1;;l: * 66 | * * 67 | * https://github.com/bblanchon/ArduinoStreamUtils * 68 | * * 69 | *****************************************************/ -------------------------------------------------------------------------------- /extras/images/Logger.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginallog -------------------------------------------------------------------------------- /examples/HammingSoftwareSerial/HammingSoftwareSerial.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to use Hamming codes for error correction 6 | // 7 | // To run this program, you need two boards connected like so: 8 | // BOARD 1 - BOARD 2 9 | // pin 10 - pin 11 10 | // pin 11 - pin 10 11 | // 12 | // SoftwareSerial is here for demonstration purposes; please don't use it in 13 | // production, as it is notoriously unreliable. 14 | 15 | #include 16 | #include 17 | 18 | SoftwareSerial linkSerial(10, 11); // RX, TX 19 | HammingStream<7, 4> eccLinkSerial(linkSerial); 20 | 21 | void setup() { 22 | Serial.begin(115200); 23 | while (!Serial) 24 | continue; 25 | 26 | linkSerial.begin(4800); 27 | } 28 | 29 | void loop() { 30 | // Did we receive something? 31 | if (eccLinkSerial.available()) 32 | // Print it 33 | Serial.write(eccLinkSerial.read()); 34 | 35 | // Do we have something to send? 36 | if (Serial.available()) 37 | // Sent it 38 | eccLinkSerial.write(Serial.read()); 39 | } 40 | 41 | /***************************************************** 42 | * * 43 | * Love this project? * 44 | * Star it on GitHub! * 45 | * * 46 | * .,,. * 47 | * ,,:1. * 48 | * ,.,:;1 * 49 | * .,,,::;: * 50 | * ,,,,::;;. * 51 | * .,,,:::;;; * 52 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 53 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 54 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 55 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 56 | * :;;:,,,,,,::::::;;;;;;l1 * 57 | * ,,,,:::::::;;;;;;l * 58 | * .,,,,::::;;;;;;;:::: * 59 | * ,,,,,:::;;;;;::,:::1 * 60 | * ,,,,,::;;;t1:,,:::::;l * 61 | * .,,,,:;;ll ;::::::;;, * 62 | * ,,,:;ll. .1:::;;l * 63 | * .,:lt, .1;;l: * 64 | * * 65 | * https://github.com/bblanchon/ArduinoStreamUtils * 66 | * * 67 | *****************************************************/ -------------------------------------------------------------------------------- /extras/images/WriteWaitingStream.svg: -------------------------------------------------------------------------------- 1 | decoratedoriginalwait() -------------------------------------------------------------------------------- /examples/HammingSerial1/HammingSerial1.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to use Hamming codes for error correction 6 | // 7 | // To run this program, you need two boards that support Serial1 with the same 8 | // voltage. For example: Arduino Mega, Leonardo, or Nano Every (all 5V boards) 9 | // 10 | // Connect the TX pin of one board to the RX pin of the other and vice-versa. 11 | 12 | #include 13 | 14 | HammingStream<7, 4> eccSerial1(Serial1); 15 | 16 | void setup() { 17 | Serial.begin(115200); 18 | while (!Serial) 19 | continue; 20 | 21 | Serial1.begin(9600); 22 | while (!Serial) 23 | continue; 24 | 25 | // Discard any remaining data in the input buffer 26 | while (Serial1.available()) 27 | Serial1.read(); 28 | } 29 | 30 | void loop() { 31 | // Did we receive something? 32 | if (eccSerial1.available()) 33 | // Print it 34 | Serial.write(eccSerial1.read()); 35 | 36 | // Do we have something to send? 37 | if (Serial.available()) 38 | // Sent it 39 | eccSerial1.write(Serial.read()); 40 | } 41 | 42 | /***************************************************** 43 | * * 44 | * Love this project? * 45 | * Star it on GitHub! * 46 | * * 47 | * .,,. * 48 | * ,,:1. * 49 | * ,.,:;1 * 50 | * .,,,::;: * 51 | * ,,,,::;;. * 52 | * .,,,:::;;; * 53 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 54 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 55 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 56 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 57 | * :;;:,,,,,,::::::;;;;;;l1 * 58 | * ,,,,:::::::;;;;;;l * 59 | * .,,,,::::;;;;;;;:::: * 60 | * ,,,,,:::;;;;;::,:::1 * 61 | * ,,,,,::;;;t1:,,:::::;l * 62 | * .,,,,:;;ll ;::::::;;, * 63 | * ,,,:;ll. .1:::;;l * 64 | * .,:lt, .1;;l: * 65 | * * 66 | * https://github.com/bblanchon/ArduinoStreamUtils * 67 | * * 68 | *****************************************************/ -------------------------------------------------------------------------------- /examples/WriteBuffer/WriteBuffer.ino: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | // 5 | // This example shows how to buffer writes to a stream. 6 | // 7 | // Like "echo," it reads from the serial port and prints back the same thing. 8 | // What's interesting with this program is that it writes in chunks of 9 | // 8 bytes, even if it seems to write them one by one. 10 | // 11 | // As you'll see, you need to type at least 8 bytes to see something. That's 12 | // because it waits for the buffered to be full before sending it to the serial. 13 | 14 | #include 15 | 16 | // Create a new Stream that buffers all writes to Serial 17 | WriteBufferingStream bufferedSerial{Serial, 8}; 18 | 19 | void setup() { 20 | // Initialize serial port 21 | Serial.begin(9600); 22 | while (!Serial) 23 | continue; 24 | 25 | Serial.println(F("Send at least 8 bytes to see the result")); 26 | } 27 | 28 | void loop() { 29 | // Even if it looks like the bytes are sent one by one, they are actual 30 | // written in chunks of 8 bytes. 31 | while (Serial.available()) { 32 | bufferedSerial.write(Serial.read()); 33 | } 34 | } 35 | 36 | /***************************************************** 37 | * * 38 | * Love this project? * 39 | * Star it on GitHub! * 40 | * * 41 | * .,,. * 42 | * ,,:1. * 43 | * ,.,:;1 * 44 | * .,,,::;: * 45 | * ,,,,::;;. * 46 | * .,,,:::;;; * 47 | * .....,,,,...,.,,,,,,:::,,,,,,,,,,,,, * 48 | * ,,,,,,,,,,,:,...,,,,,,:::,,,,:::;;;11l * 49 | * .;::::::::,,,,,,,,,,:::::,,::;;;1lt * 50 | * .;;;:::,,,,,,,,::::::;:::;;1t: * 51 | * :;;:,,,,,,::::::;;;;;;l1 * 52 | * ,,,,:::::::;;;;;;l * 53 | * .,,,,::::;;;;;;;:::: * 54 | * ,,,,,:::;;;;;::,:::1 * 55 | * ,,,,,::;;;t1:,,:::::;l * 56 | * .,,,,:;;ll ;::::::;;, * 57 | * ,,,:;ll. .1:::;;l * 58 | * .,:lt, .1;;l: * 59 | * * 60 | * https://github.com/bblanchon/ArduinoStreamUtils * 61 | * * 62 | *****************************************************/ -------------------------------------------------------------------------------- /src/StreamUtils/Buffers/LinearBuffer.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "../Helpers.hpp" 11 | #include "CharArray.hpp" 12 | 13 | namespace StreamUtils { 14 | 15 | template 16 | class LinearBuffer { 17 | public: 18 | LinearBuffer(size_t capacity, TAllocator allocator = TAllocator()) 19 | : _data(capacity, allocator) { 20 | _begin = _data ? &_data : nullptr; 21 | _end = _begin; 22 | } 23 | 24 | LinearBuffer(const LinearBuffer &src) : _data(src._data) { 25 | if (_data) { 26 | memcpy(&_data, src._begin, src.available()); 27 | _begin = &_data; 28 | _end = &_data + src.available(); 29 | } else { 30 | _begin = nullptr; 31 | _end = nullptr; 32 | } 33 | } 34 | 35 | size_t available() const { 36 | return _end - _begin; 37 | } 38 | 39 | size_t capacity() const { 40 | return _data.size(); 41 | } 42 | 43 | void clear() { 44 | _begin = _end = _data; 45 | } 46 | 47 | bool isEmpty() const { 48 | return available() == 0; 49 | } 50 | 51 | bool isFull() const { 52 | return available() == capacity(); 53 | } 54 | 55 | operator bool() const { 56 | return _data; 57 | } 58 | 59 | char peek() const { 60 | return *_begin; 61 | } 62 | 63 | char read() { 64 | return *_begin++; 65 | } 66 | 67 | size_t readBytes(char *dstPtr, size_t dstSize) { 68 | size_t srcSize = available(); 69 | size_t n = srcSize < dstSize ? srcSize : dstSize; 70 | memcpy(dstPtr, _begin, n); 71 | _begin += n; 72 | return n; 73 | } 74 | 75 | size_t write(uint8_t data) { 76 | assert(!isFull()); 77 | *_end++ = data; 78 | return 1; 79 | } 80 | 81 | size_t write(const uint8_t *data, size_t size) { 82 | size_t roomLeft = capacity() - available(); 83 | if (size > roomLeft) 84 | size = roomLeft; 85 | memcpy(_end, data, size); 86 | _end += size; 87 | return size; 88 | } 89 | 90 | template // Stream or Client 91 | void reloadFrom(TTarget &source, size_t size) { 92 | if (size > _data.size()) 93 | size = _data.size(); 94 | size_t n = readOrReadBytes(source, &_data, size); 95 | _begin = &_data; 96 | _end = &_data + n; 97 | } 98 | 99 | void flushInto(Print &destination) { 100 | if (_begin != _end) 101 | destination.write(_begin, _end - _begin); 102 | _begin = _end = &_data; 103 | } 104 | 105 | private: 106 | CharArray _data; 107 | char *_begin; 108 | char *_end; 109 | }; 110 | 111 | } // namespace StreamUtils -------------------------------------------------------------------------------- /src/StreamUtils/Policies/ReadBufferingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Buffers/LinearBuffer.hpp" 10 | #include "../Helpers.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | struct ReadBufferingPolicy { 16 | ReadBufferingPolicy(size_t capacity, TAllocator allocator = TAllocator()) 17 | : _buffer(capacity, allocator) {} 18 | 19 | ReadBufferingPolicy(const ReadBufferingPolicy &other) 20 | : _buffer(other._buffer) {} 21 | 22 | int available(Stream &stream) { 23 | return static_cast(stream.available() + _buffer.available()); 24 | } 25 | 26 | template // Stream or Client 27 | int read(TTarget &target) { 28 | if (!_buffer) 29 | return target.read(); 30 | 31 | if (_buffer.available() > 0) 32 | return _buffer.read(); 33 | 34 | size_t avail = static_cast(target.available()); 35 | if (avail <= 1) 36 | return target.read(); 37 | 38 | _buffer.reloadFrom(target, avail); 39 | return _buffer.read(); 40 | } 41 | 42 | int peek(Stream &stream) { 43 | return isEmpty() ? stream.peek() : _buffer.peek(); 44 | } 45 | 46 | size_t readBytes(Stream &stream, char *buffer, size_t size) { 47 | return doReadBytes(stream, buffer, size); 48 | } 49 | 50 | int read(Client &client, uint8_t *buffer, size_t size) { 51 | return static_cast( 52 | doReadBytes(client, reinterpret_cast(buffer), size)); 53 | } 54 | 55 | private: 56 | bool isEmpty() const { 57 | return _buffer.available() == 0; 58 | } 59 | 60 | LinearBuffer _buffer; 61 | 62 | template // Stream or Client 63 | size_t doReadBytes(TTarget &target, char *buffer, size_t size) { 64 | if (!_buffer) 65 | return readOrReadBytes(target, buffer, size); 66 | 67 | size_t result = 0; 68 | 69 | // can we read from buffer? 70 | if (_buffer.available() > 0) { 71 | size_t bytesRead = _buffer.readBytes(buffer, size); 72 | result += bytesRead; 73 | buffer += bytesRead; 74 | size -= bytesRead; 75 | } 76 | 77 | // still something to read? 78 | if (size > 0) { 79 | // (at this point, the buffer is empty) 80 | 81 | size_t avail = static_cast(target.available()); 82 | 83 | // should we use the buffer? 84 | if (avail > size && size < _buffer.capacity()) { 85 | _buffer.reloadFrom(target, avail); 86 | size_t bytesRead = _buffer.readBytes(buffer, size); 87 | result += bytesRead; 88 | } else { 89 | // we can bypass the buffer 90 | result += readOrReadBytes(target, buffer, size); 91 | } 92 | } 93 | 94 | return result; 95 | } 96 | }; // namespace StreamUtils 97 | 98 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/ProgmemStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Streams/ProgmemStream.hpp" 6 | 7 | #include "doctest.h" 8 | 9 | using namespace StreamUtils; 10 | 11 | TEST_CASE("ProgmemStream") { 12 | GIVEN("constructor was called with {0}") { 13 | ProgmemStream s{static_cast(0)}; 14 | 15 | THEN("available() should return 0") { 16 | CHECK(s.available() == 0); 17 | } 18 | 19 | THEN("peek() should return -1") { 20 | CHECK(s.peek() == -1); 21 | } 22 | 23 | THEN("read() should return -1") { 24 | CHECK(s.read() == -1); 25 | } 26 | 27 | THEN("readBytes() should return 0") { 28 | char buffer[8]; 29 | CHECK(s.readBytes(buffer, 8) == 0); 30 | } 31 | } 32 | 33 | GIVEN("constructor was called with {F(\"hello\"))}") { 34 | ProgmemStream s{F("hello")}; 35 | 36 | THEN("available() should return 5") { 37 | CHECK(s.available() == 5); 38 | } 39 | } 40 | 41 | GIVEN("constructor was called with {F(\"hello\"), 5)}") { 42 | ProgmemStream s(F("hello"), 5); 43 | 44 | THEN("write(char) should return 0") { 45 | CHECK(s.write('a') == 0); 46 | } 47 | 48 | THEN("print(const char*) should return 0") { 49 | CHECK(s.print("abc") == 0); 50 | } 51 | 52 | THEN("available() should return 5") { 53 | CHECK(s.available() == 5); 54 | } 55 | 56 | THEN("peek() should return 'h'") { 57 | CHECK(s.peek() == 'h'); 58 | } 59 | 60 | WHEN("read() is called") { 61 | int c = s.read(); 62 | 63 | THEN("it sould return 'h'") { 64 | CHECK(c == 'h'); 65 | } 66 | 67 | THEN("available() should return 4") { 68 | CHECK(s.available() == 4); 69 | } 70 | 71 | THEN("peek() should return 'e'") { 72 | CHECK(s.peek() == 'e'); 73 | } 74 | } 75 | 76 | WHEN("readBytes(buffer, 4) is called") { 77 | char buffer[4]; 78 | int n = s.readBytes(buffer, 4); 79 | 80 | THEN("it should return 4") { 81 | CHECK(n == 4); 82 | } 83 | 84 | THEN("available() should return 1") { 85 | CHECK(s.available() == 1); 86 | } 87 | 88 | THEN("peek() should return 'o'") { 89 | CHECK(s.peek() == 'o'); 90 | } 91 | 92 | THEN("read() should return 'o'") { 93 | CHECK(s.read() == 'o'); 94 | } 95 | } 96 | 97 | WHEN("readBytes(buffer, 8) is called") { 98 | char buffer[8]; 99 | int n = s.readBytes(buffer, 8); 100 | 101 | THEN("it should return 5") { 102 | CHECK(n == 5); 103 | } 104 | 105 | THEN("available() should return 0") { 106 | CHECK(s.available() == 0); 107 | } 108 | 109 | THEN("peek() should return -1") { 110 | CHECK(s.peek() == -1); 111 | } 112 | 113 | THEN("read() should return -1") { 114 | CHECK(s.read() == -1); 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /extras/test/WaitingPrintTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "FailingAllocator.hpp" 6 | 7 | #include "StreamUtils/Prints/StringPrint.hpp" 8 | #include "StreamUtils/Prints/WaitingPrint.hpp" 9 | #include "StreamUtils/Streams/MemoryStream.hpp" 10 | #include "StreamUtils/Streams/SpyingStream.hpp" 11 | 12 | #include "doctest.h" 13 | 14 | using namespace StreamUtils; 15 | 16 | TEST_CASE("WaitingPrint") { 17 | MemoryStream upstream(4); 18 | 19 | StringPrint log; 20 | SpyingStream spy{upstream, log}; 21 | WaitingPrint stream{static_cast(spy), [&spy]() { spy.flush(); }}; 22 | 23 | SUBCASE("write(char*, size_t)") { 24 | SUBCASE("no need to wait") { 25 | CHECK(stream.print("ABC") == 3); 26 | 27 | CHECK(log.str() == "write('ABC', 3) -> 3"); 28 | } 29 | 30 | SUBCASE("need to wait") { 31 | CHECK(stream.print("ABCDEFG") == 7); 32 | 33 | CHECK(log.str() == 34 | "write('ABCDEFG', 7) -> 4" 35 | "flush()" 36 | "write('EFG', 3) -> 3"); 37 | } 38 | 39 | SUBCASE("need to wait twice") { 40 | CHECK(stream.print("ABCDEFGIJKL") == 11); 41 | 42 | CHECK(log.str() == 43 | "write('ABCDEFGIJKL', 11) -> 4" 44 | "flush()" 45 | "write('EFGIJKL', 7) -> 4" 46 | "flush()" 47 | "write('JKL', 3) -> 3"); 48 | } 49 | 50 | SUBCASE("doesn't wait when timeout is 0") { 51 | stream.setTimeout(0); 52 | 53 | CHECK(stream.print("ABCDEFG") == 4); 54 | 55 | CHECK(log.str() == "write('ABCDEFG', 7) -> 4"); 56 | } 57 | } 58 | 59 | SUBCASE("write(char)") { 60 | SUBCASE("no need to wait") { 61 | CHECK(stream.write('A') == 1); 62 | CHECK(stream.write('B') == 1); 63 | CHECK(stream.write('C') == 1); 64 | 65 | CHECK(log.str() == 66 | "write('A') -> 1" 67 | "write('B') -> 1" 68 | "write('C') -> 1"); 69 | } 70 | 71 | SUBCASE("need to wait") { 72 | for (int i = 0; i < 7; i++) 73 | CHECK(stream.write("ABCDEFG"[i]) == 1); 74 | 75 | CHECK(log.str() == 76 | "write('A') -> 1" 77 | "write('B') -> 1" 78 | "write('C') -> 1" 79 | "write('D') -> 1" 80 | "write('E') -> 0" 81 | "flush()" 82 | "write('E') -> 1" 83 | "write('F') -> 1" 84 | "write('G') -> 1"); 85 | } 86 | 87 | SUBCASE("doesn't wait when timeout is 0") { 88 | stream.setTimeout(0); 89 | 90 | CHECK(stream.write('A') == 1); 91 | CHECK(stream.write('B') == 1); 92 | CHECK(stream.write('C') == 1); 93 | CHECK(stream.write('D') == 1); 94 | CHECK(stream.write('E') == 0); 95 | 96 | CHECK(log.str() == 97 | "write('A') -> 1" 98 | "write('B') -> 1" 99 | "write('C') -> 1" 100 | "write('D') -> 1" 101 | "write('E') -> 0"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /extras/test/WriteWaitingClientTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "FailingAllocator.hpp" 6 | 7 | #include "StreamUtils/Clients/MemoryClient.hpp" 8 | #include "StreamUtils/Clients/SpyingClient.hpp" 9 | #include "StreamUtils/Clients/WriteWaitingClient.hpp" 10 | #include "StreamUtils/Prints/StringPrint.hpp" 11 | 12 | #include "doctest.h" 13 | 14 | using namespace StreamUtils; 15 | 16 | TEST_CASE("WriteWaitingClient") { 17 | MemoryClient upstream(4); 18 | 19 | StringPrint log; 20 | SpyingClient spy{upstream, log}; 21 | WriteWaitingClient client{spy, [&spy]() { spy.flush(); }}; 22 | 23 | SUBCASE("write(char*, size_t)") { 24 | SUBCASE("no need to wait") { 25 | CHECK(client.print("ABC") == 3); 26 | 27 | CHECK(log.str() == "write('ABC', 3) -> 3"); 28 | } 29 | 30 | SUBCASE("need to wait") { 31 | CHECK(client.print("ABCDEFG") == 7); 32 | 33 | CHECK(log.str() == 34 | "write('ABCDEFG', 7) -> 4" 35 | "flush()" 36 | "write('EFG', 3) -> 3"); 37 | } 38 | 39 | SUBCASE("need to wait twice") { 40 | CHECK(client.print("ABCDEFGIJKL") == 11); 41 | 42 | CHECK(log.str() == 43 | "write('ABCDEFGIJKL', 11) -> 4" 44 | "flush()" 45 | "write('EFGIJKL', 7) -> 4" 46 | "flush()" 47 | "write('JKL', 3) -> 3"); 48 | } 49 | 50 | SUBCASE("doesn't wait when timeout is 0") { 51 | client.setTimeout(0); 52 | 53 | CHECK(client.print("ABCDEFG") == 4); 54 | 55 | CHECK(log.str() == "write('ABCDEFG', 7) -> 4"); 56 | } 57 | } 58 | 59 | SUBCASE("write(char)") { 60 | SUBCASE("no need to wait") { 61 | CHECK(client.write('A') == 1); 62 | CHECK(client.write('B') == 1); 63 | CHECK(client.write('C') == 1); 64 | 65 | CHECK(log.str() == 66 | "write('A') -> 1" 67 | "write('B') -> 1" 68 | "write('C') -> 1"); 69 | } 70 | 71 | SUBCASE("need to wait") { 72 | for (int i = 0; i < 7; i++) 73 | CHECK(client.write("ABCDEFG"[i]) == 1); 74 | 75 | CHECK(log.str() == 76 | "write('A') -> 1" 77 | "write('B') -> 1" 78 | "write('C') -> 1" 79 | "write('D') -> 1" 80 | "write('E') -> 0" 81 | "flush()" 82 | "write('E') -> 1" 83 | "write('F') -> 1" 84 | "write('G') -> 1"); 85 | } 86 | 87 | SUBCASE("doesn't wait when timeout is 0") { 88 | client.setTimeout(0); 89 | 90 | CHECK(client.write('A') == 1); 91 | CHECK(client.write('B') == 1); 92 | CHECK(client.write('C') == 1); 93 | CHECK(client.write('D') == 1); 94 | CHECK(client.write('E') == 0); 95 | 96 | CHECK(log.str() == 97 | "write('A') -> 1" 98 | "write('B') -> 1" 99 | "write('C') -> 1" 100 | "write('D') -> 1" 101 | "write('E') -> 0"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /extras/test/WriteWaitingStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "FailingAllocator.hpp" 6 | 7 | #include "StreamUtils/Prints/StringPrint.hpp" 8 | #include "StreamUtils/Streams/MemoryStream.hpp" 9 | #include "StreamUtils/Streams/SpyingStream.hpp" 10 | #include "StreamUtils/Streams/WriteWaitingStream.hpp" 11 | 12 | #include "doctest.h" 13 | 14 | using namespace StreamUtils; 15 | 16 | TEST_CASE("WriteWaitingStream") { 17 | MemoryStream upstream(4); 18 | 19 | StringPrint log; 20 | SpyingStream spy{upstream, log}; 21 | WriteWaitingStream stream{spy, [&spy]() { spy.flush(); }}; 22 | 23 | SUBCASE("write(char*, size_t)") { 24 | SUBCASE("no need to wait") { 25 | CHECK(stream.print("ABC") == 3); 26 | 27 | CHECK(log.str() == "write('ABC', 3) -> 3"); 28 | } 29 | 30 | SUBCASE("need to wait") { 31 | CHECK(stream.print("ABCDEFG") == 7); 32 | 33 | CHECK(log.str() == 34 | "write('ABCDEFG', 7) -> 4" 35 | "flush()" 36 | "write('EFG', 3) -> 3"); 37 | } 38 | 39 | SUBCASE("need to wait twice") { 40 | CHECK(stream.print("ABCDEFGIJKL") == 11); 41 | 42 | CHECK(log.str() == 43 | "write('ABCDEFGIJKL', 11) -> 4" 44 | "flush()" 45 | "write('EFGIJKL', 7) -> 4" 46 | "flush()" 47 | "write('JKL', 3) -> 3"); 48 | } 49 | 50 | SUBCASE("doesn't wait when timeout is 0") { 51 | stream.setTimeout(0); 52 | 53 | CHECK(stream.print("ABCDEFG") == 4); 54 | 55 | CHECK(log.str() == "write('ABCDEFG', 7) -> 4"); 56 | } 57 | } 58 | 59 | SUBCASE("write(char)") { 60 | SUBCASE("no need to wait") { 61 | CHECK(stream.write('A') == 1); 62 | CHECK(stream.write('B') == 1); 63 | CHECK(stream.write('C') == 1); 64 | 65 | CHECK(log.str() == 66 | "write('A') -> 1" 67 | "write('B') -> 1" 68 | "write('C') -> 1"); 69 | } 70 | 71 | SUBCASE("need to wait") { 72 | for (int i = 0; i < 7; i++) 73 | CHECK(stream.write("ABCDEFG"[i]) == 1); 74 | 75 | CHECK(log.str() == 76 | "write('A') -> 1" 77 | "write('B') -> 1" 78 | "write('C') -> 1" 79 | "write('D') -> 1" 80 | "write('E') -> 0" 81 | "flush()" 82 | "write('E') -> 1" 83 | "write('F') -> 1" 84 | "write('G') -> 1"); 85 | } 86 | 87 | SUBCASE("doesn't wait when timeout is 0") { 88 | stream.setTimeout(0); 89 | 90 | CHECK(stream.write('A') == 1); 91 | CHECK(stream.write('B') == 1); 92 | CHECK(stream.write('C') == 1); 93 | CHECK(stream.write('D') == 1); 94 | CHECK(stream.write('E') == 0); 95 | 96 | CHECK(log.str() == 97 | "write('A') -> 1" 98 | "write('B') -> 1" 99 | "write('C') -> 1" 100 | "write('D') -> 1" 101 | "write('E') -> 0"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/StreamUtils/Clients/ClientProxy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "../Configuration.hpp" 10 | #include "../Streams/StreamProxy.hpp" 11 | 12 | namespace StreamUtils { 13 | 14 | template 15 | class ClientProxy : public Client { 16 | public: 17 | explicit ClientProxy(Client &upstream, ReadPolicy reader = ReadPolicy(), 18 | WritePolicy writer = WritePolicy(), 19 | ConnectPolicy connection = ConnectPolicy()) 20 | : _target(upstream), 21 | _reader(reader), 22 | _writer(Polyfills::move(writer)), 23 | _connection(connection) {} 24 | 25 | ClientProxy(const ClientProxy &other) 26 | : _target(other._target), 27 | _reader(other._reader), 28 | _writer(other._writer), 29 | _connection(other._connection) {} 30 | 31 | ~ClientProxy() { 32 | _writer.implicitFlush(_target); 33 | } 34 | 35 | // --- Print --- 36 | 37 | size_t write(const uint8_t *buffer, size_t size) override { 38 | return _writer.write(_target, buffer, size); 39 | } 40 | 41 | size_t write(uint8_t data) override { 42 | return _writer.write(_target, data); 43 | } 44 | 45 | #if STREAMUTILS_PRINT_WRITE_VOID_UINT32 46 | size_t write(const void *buffer, uint32 size) override { 47 | return write(reinterpret_cast(buffer), 48 | static_cast(size)); 49 | } 50 | #endif 51 | 52 | using Print::write; 53 | 54 | // --- Stream --- 55 | 56 | int available() override { 57 | return _reader.available(_target); 58 | } 59 | 60 | int read() override { 61 | return _reader.read(_target); 62 | } 63 | 64 | int peek() override { 65 | return _reader.peek(_target); 66 | } 67 | 68 | #if STREAMUTILS_STREAM_READBYTES_IS_VIRTUAL 69 | size_t readBytes(char *buffer, size_t size) override { 70 | return _reader.readBytes(_target, buffer, size); 71 | } 72 | #endif 73 | 74 | // --- Client --- 75 | 76 | int connect(IPAddress ip, uint16_t port) override { 77 | return _connection.connect(_target, ip, port); 78 | } 79 | 80 | int connect(const char *ip, uint16_t port) override { 81 | return _connection.connect(_target, ip, port); 82 | } 83 | 84 | uint8_t connected() override { 85 | return _connection.connected(_target); 86 | } 87 | 88 | void stop() override { 89 | _writer.implicitFlush(_target); 90 | _connection.stop(_target); 91 | } 92 | 93 | operator bool() override { 94 | return _connection.operator_bool(_target); 95 | } 96 | 97 | int read(uint8_t *buf, size_t size) override { 98 | return _reader.read(_target, buf, size); 99 | } 100 | 101 | void flush() override { 102 | _writer.flush(_target); 103 | } 104 | 105 | protected: 106 | Client &_target; 107 | ReadPolicy _reader; 108 | WritePolicy _writer; 109 | ConnectPolicy _connection; 110 | }; 111 | 112 | } // namespace StreamUtils -------------------------------------------------------------------------------- /extras/test/MemoryStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Streams/MemoryStream.hpp" 6 | #include "StreamUtils/Streams/SpyingStream.hpp" 7 | 8 | #include "doctest.h" 9 | 10 | using namespace StreamUtils; 11 | 12 | TEST_CASE("MemoryStream") { 13 | MemoryStream stream(4); 14 | 15 | WHEN("stream is empty") { 16 | THEN("available() return 0") { 17 | CHECK(stream.available() == 0); 18 | } 19 | 20 | THEN("write(uint8) returns 1") { 21 | CHECK(stream.write('A') == 1); 22 | } 23 | 24 | THEN("write(\"ABCD\",4) returns 4") { 25 | CHECK(stream.write("ABCD", 4) == 4); 26 | } 27 | 28 | THEN("write(uint8*,8) returns 4") { 29 | CHECK(stream.write("ABCDEFGH", 8) == 4); 30 | } 31 | 32 | THEN("read() return -1") { 33 | CHECK(stream.read() == -1); 34 | } 35 | 36 | THEN("peek() return -1") { 37 | CHECK(stream.peek() == -1); 38 | } 39 | } 40 | 41 | WHEN("stream is full") { 42 | stream.print("ABCD"); 43 | 44 | THEN("available() return 4") { 45 | CHECK(stream.available() == 4); 46 | } 47 | 48 | THEN("write(uint8) returns 0") { 49 | CHECK(stream.write('A') == 0); 50 | } 51 | 52 | THEN("write(\"ABCD\",4) returns 0") { 53 | CHECK(stream.write("ABCD", 4) == 0); 54 | } 55 | 56 | THEN("read() returns the next value") { 57 | CHECK(stream.read() == 'A'); 58 | CHECK(stream.read() == 'B'); 59 | } 60 | 61 | THEN("read(uint8_t*,size_t) extracts the next bytes") { 62 | char data[5] = {0}; 63 | CHECK(stream.readBytes(data, 4) == 4); 64 | CHECK(data == std::string("ABCD")); 65 | } 66 | 67 | THEN("peek() returns the first value") { 68 | CHECK(stream.peek() == 'A'); 69 | CHECK(stream.peek() == 'A'); 70 | } 71 | 72 | SUBCASE("read() makes room for one byte") { 73 | stream.read(); // make room 74 | REQUIRE(stream.available() == 3); 75 | 76 | stream.write('E'); // write at the beginning 77 | REQUIRE(stream.available() == 4); 78 | 79 | char data[5] = {0}; 80 | stream.readBytes(data, 4); 81 | CHECK(data == std::string("BCDE")); 82 | } 83 | } 84 | 85 | WHEN("lower half is filled") { 86 | stream.print("AB"); 87 | 88 | THEN("available() return 2") { 89 | CHECK(stream.available() == 2); 90 | } 91 | 92 | THEN("write(uint8) returns 1") { 93 | CHECK(stream.write('C') == 1); 94 | } 95 | 96 | THEN("write(\"ABCD\",4) returns 2") { 97 | CHECK(stream.write("ABCD", 4) == 2); 98 | } 99 | 100 | THEN("read() returns the next value") { 101 | CHECK(stream.read() == 'A'); 102 | CHECK(stream.read() == 'B'); 103 | } 104 | 105 | THEN("read(uint8_t*,size_t) extracts the next bytes") { 106 | char data[5] = {0}; 107 | CHECK(stream.readBytes(data, 4) == 2); 108 | CHECK(data == std::string("AB")); 109 | } 110 | 111 | THEN("peek() returns the first value") { 112 | CHECK(stream.peek() == 'A'); 113 | CHECK(stream.peek() == 'A'); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/StreamUtils/Policies/HammingEncodingPolicy.hpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #pragma once 6 | 7 | #if defined(WIN32) || defined(__WIN32) || defined(__WIN32__) 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | #include "../Configuration.hpp" 14 | 15 | namespace StreamUtils { 16 | 17 | template 18 | class HammingEncodingPolicy; 19 | 20 | template 21 | class HammingEncodingPolicy<7, 4, TAllocator> { 22 | const static size_t sizeAllowedOnStack = STREAMUTILS_STACK_BUFFER_MAX_SIZE; 23 | 24 | public: 25 | HammingEncodingPolicy(TAllocator allocator = TAllocator()) 26 | : _allocator(allocator) {} 27 | 28 | size_t write(Print &target, const uint8_t *data, size_t size) { 29 | if (!flushRemainder(target)) 30 | return 0; 31 | 32 | size_t bufferSize = size * 2; 33 | uint8_t *buffer; 34 | if (bufferSize > sizeAllowedOnStack) { 35 | buffer = static_cast(_allocator.allocate(bufferSize)); 36 | if (!buffer) { 37 | bufferSize = sizeAllowedOnStack; 38 | buffer = static_cast(alloca(bufferSize)); 39 | } 40 | } else { 41 | buffer = static_cast(alloca(bufferSize)); 42 | } 43 | 44 | for (size_t i = 0, j = 0; j < bufferSize; i++) { 45 | buffer[j++] = encode(data[i] >> 4); 46 | buffer[j++] = encode(data[i] & 0x0f); 47 | } 48 | size_t n = target.write(buffer, bufferSize); 49 | if (n & 1) { 50 | _remainder = buffer[n]; 51 | ++n; 52 | } 53 | 54 | if (bufferSize > sizeAllowedOnStack) 55 | _allocator.deallocate(buffer); 56 | 57 | return n / 2; 58 | } 59 | 60 | size_t write(Print &target, uint8_t data) { 61 | uint8_t first = encode(data >> 4); 62 | uint8_t second = encode(data & 0x0f); 63 | 64 | if (!flushRemainder(target)) 65 | return 0; 66 | 67 | if (!target.write(first)) 68 | return 0; 69 | 70 | if (!target.write(second)) 71 | _remainder = second; 72 | 73 | return 1; 74 | } 75 | 76 | void flush(Stream &target) { 77 | flushRemainder(target); 78 | target.flush(); 79 | } 80 | 81 | void flush(Print &target) { 82 | flushRemainder(target); 83 | #if STREAMUTILS_PRINT_FLUSH_EXISTS 84 | target.flush(); 85 | #endif 86 | } 87 | 88 | void implicitFlush(Print &target) { 89 | flushRemainder(target); 90 | } 91 | 92 | private: 93 | // Encode 4-bits to 7-bits using Hamming(7,4) 94 | uint8_t encode(uint8_t input) { 95 | static uint8_t table[] = {0x00, 0x71, 0x62, 0x13, 0x54, 0x25, 0x36, 0x47, 96 | 0x38, 0x49, 0x5A, 0x2B, 0x6C, 0x1D, 0x0E, 0x7F}; 97 | return table[input]; 98 | } 99 | 100 | template 101 | bool flushRemainder(TTarget &target) { 102 | if (_remainder < 0) 103 | return true; 104 | 105 | if (!target.write(_remainder)) 106 | return false; 107 | 108 | _remainder = -1; 109 | return true; 110 | } 111 | 112 | TAllocator _allocator; 113 | int8_t _remainder = -1; 114 | }; 115 | 116 | } // namespace StreamUtils -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | StreamUtils - Change log 2 | ======================== 3 | 4 | 1.9.1 (2024/11/05) 5 | ---- 6 | 7 | * Fix infinite loop in `ChunkDecodingPolicy` when transmission cuts mid-chunk (issue #37) 8 | 9 | 1.9.0 (2024/07/10) 10 | ----- 11 | 12 | * Add `ChunkDecodingStream` and `ChunkDecodingClient` 13 | 14 | 1.8.0 (2023/12/05) 15 | ----- 16 | 17 | * Add support for DxCore (issue #34) 18 | 19 | 1.7.3 (2023/03/08) 20 | ----- 21 | 22 | * Fix "EEPROM.h: No such file or directory" with ArduinoCore-mbed (issue #31) 23 | 24 | 1.7.2 (2023/03/04) 25 | ----- 26 | 27 | * Fix "__FlashStringHelper is ambiguous" with arduino-pico (issue #30) 28 | 29 | 1.7.1 (2023/02/26) 30 | ----- 31 | 32 | * Fix `HammingStream` and `HammingDecodingStream` on ESP32 (issue #29) 33 | 34 | 1.7.0 (2022/11/14) 35 | ----- 36 | 37 | * Add support for `Print::flush()` on ESP32 and STM32 38 | * Add `ProgmemStream` 39 | 40 | 1.6.3 (2022/05/11) 41 | ----- 42 | 43 | * Add support for Roger's core for STM32 (issue #20) 44 | 45 | 1.6.2 (2022/02/09) 46 | ----- 47 | 48 | * Support `Print::flush()` on AVR 49 | * Fix `StringStream.readBytes()` on ESP8266, ESP32, and STM32 (issue #16) 50 | 51 | 1.6.1 (2021/04/05) 52 | ----- 53 | 54 | * Add example `HammingSerial1.ino` 55 | * Add support for STM32 (issue #11) 56 | 57 | 1.6.0 (2020/11/20) 58 | ----- 59 | 60 | * Add `HammingPrint<7, 4>` 61 | * Add `HammingStream<7, 4>`, `HammingEncodingStream<7, 4>`, and `HammingDecodingStream<7, 4>` 62 | * Add `HammingClient<7, 4>`, `HammingEncodingClient<7, 4>`, and `HammingDecodingClient<7, 4>` 63 | 64 | 1.5.0 (2020/08/04) 65 | ----- 66 | 67 | * Add `WaitingPrint`, `WriteWaitingClient`, and `WriteWaitingStream`. 68 | 69 | 1.4.1 (2020/07/01) 70 | ----- 71 | 72 | * Fix unwanted waits in `ReadBufferingClient` and `ReadBufferingStream`. 73 | * Stop calling `Client::read()` in place of `Stream::readBytes()`, 74 | because it doesn't honor the timeout. 75 | 76 | 1.4.0 (2020/03/30) 77 | ----- 78 | 79 | * Add `EepromStream` 80 | * Add support for ESP32 81 | * Add support for Teensy 82 | 83 | 1.3.0 (2020/01/20) 84 | ----- 85 | 86 | * Move auxiliary content to `extras/` to comply with new library layout 87 | * Add `StringPrint` and `StringStream` 88 | * Extract `StreamUtils.hpp`, same as `StreamUtils.h` except it keeps everything in the `StreamUtils` namespace. 89 | 90 | 1.2.2 (2019/07/18) 91 | ----- 92 | 93 | * Fix `BufferingPrint` taking `Stream` instead of `Print` (issue #3) 94 | * Fix `LoggingPrint` not forwarding call to `Print::flush()` 95 | * Fix missing `override` specifiers 96 | 97 | 1.2.1 (2019/06/05) 98 | ----- 99 | 100 | * Remove workaround for ESP8266 core 2.5.0 101 | * Fix compatibility with ESP8266 core 2.5.1+ (issue #2) 102 | 103 | 1.2.0 (2019/05/01) 104 | ----- 105 | 106 | * Add `LoggingPrint` 107 | * Add `BufferingPrint` 108 | * Add `WriteLoggingClient`, `ReadLoggingClient`, and `LoggingClient` 109 | * Add `WriteBufferingClient` and `ReadBufferingClient` 110 | 111 | 1.1.0 (2019/04/20) 112 | ----- 113 | 114 | * Add `LoggingStream` (=`ReadLoggingStream` + `WriteLoggingStream`) 115 | 116 | 1.0.0 (2019/04/14) 117 | ----- 118 | 119 | * Add `ReadBufferingStream` and `WriteBufferingStream` 120 | * Add `ReadLoggingStream` and `WriteLoggingStream` 121 | -------------------------------------------------------------------------------- /extras/test/StringStreamTest.cpp: -------------------------------------------------------------------------------- 1 | // StreamUtils - github.com/bblanchon/ArduinoStreamUtils 2 | // Copyright Benoit Blanchon 2019-2024 3 | // MIT License 4 | 5 | #include "StreamUtils/Streams/StringStream.hpp" 6 | 7 | #include "doctest.h" 8 | 9 | using namespace StreamUtils; 10 | 11 | TEST_CASE("StringStream") { 12 | WHEN("Constructed with no argument") { 13 | StringStream s; 14 | 15 | THEN("str() return an empty String") { 16 | CHECK(s.str() == ""); 17 | } 18 | 19 | THEN("write(uint8_t) appends the string") { 20 | s.write('A'); 21 | s.write('B'); 22 | CHECK(s.str() == "AB"); 23 | } 24 | 25 | THEN("write(uint8_t) return 1") { 26 | CHECK(s.write('A') == 1); 27 | CHECK(s.write('B') == 1); 28 | } 29 | 30 | THEN("write(0) return 0") { 31 | CHECK(s.write(0) == 0); 32 | } 33 | 34 | THEN("write(uint8_t*, size_t) appends the string") { 35 | s.write(reinterpret_cast("ABXXX"), 2); 36 | s.write(reinterpret_cast("CDEXX"), 3); 37 | CHECK(s.str() == "ABCDE"); 38 | } 39 | 40 | THEN("write(uint8_t*, size_t) return number of chars written") { 41 | uint8_t dummy[32] = {1, 2, 3, 0, 5, 6}; 42 | CHECK(s.write(dummy, 2) == 2); 43 | CHECK(s.write(dummy, 3) == 3); 44 | CHECK(s.write(dummy, 4) == 3); 45 | } 46 | 47 | THEN("str(String) sets the string") { 48 | s.str("world!"); 49 | CHECK(s.str() == "world!"); 50 | } 51 | 52 | THEN("available() returns 0") { 53 | CHECK(s.available() == 0); 54 | } 55 | 56 | THEN("peek() return -1") { 57 | CHECK(s.peek() == -1); 58 | } 59 | 60 | THEN("peek() return -1") { 61 | CHECK(s.read() == -1); 62 | } 63 | } 64 | 65 | WHEN("Constructed with string") { 66 | StringStream s("hello"); 67 | 68 | THEN("str() returns the string passed to contructor") { 69 | CHECK(s.str() == "hello"); 70 | } 71 | 72 | THEN("available() returns the length passed to the constructor") { 73 | CHECK(s.available() == 5); 74 | } 75 | 76 | THEN("write(uint8_t) appends the string") { 77 | s.write('A'); 78 | s.write('B'); 79 | CHECK(s.str() == "helloAB"); 80 | } 81 | 82 | THEN("write(uint8_t*, size_t) appends the string") { 83 | s.write(reinterpret_cast("ABXXX"), 2); 84 | s.write(reinterpret_cast("CDEXX"), 3); 85 | CHECK(s.str() == "helloABCDE"); 86 | } 87 | 88 | THEN("str(String) replaces the string") { 89 | s.str("world!"); 90 | CHECK(s.str() == "world!"); 91 | } 92 | 93 | THEN("peek() return the first char") { 94 | CHECK(s.peek() == 'h'); 95 | } 96 | 97 | THEN("peek() return the next char") { 98 | CHECK(s.read() == 'h'); 99 | CHECK(s.read() == 'e'); 100 | } 101 | 102 | THEN("readBytes() can return the begining of the string") { 103 | char buffer[32] = {0}; 104 | CHECK(s.readBytes(buffer, 3) == 3); 105 | CHECK(buffer == std::string("hel")); 106 | CHECK(s.available() == 2); 107 | } 108 | 109 | THEN("readBytes() can return the whole string") { 110 | char buffer[32] = {0}; 111 | CHECK(s.readBytes(buffer, 32) == 5); 112 | CHECK(buffer == std::string("hello")); 113 | CHECK(s.available() == 0); 114 | } 115 | } 116 | } 117 | --------------------------------------------------------------------------------