/** StreamString.h Copyright (c) 2020 D. Gauchard. All rights reserved. This file is part of the esp8266 core for Arduino environment. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __STREAMSTRING_H #define __STREAMSTRING_H #include #include #include "Stream.h" #include "api/String.h" /////////////////////////////////////////////////////////////// // S2Stream points to a String and makes it a Stream // (it is also the helper for StreamString) class S2Stream: public Stream { public: S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { } S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { } virtual int available() override { return string->length(); } virtual int availableForWrite() override { return std::numeric_limits::max(); } virtual int read() override { if (peekPointer < 0) { // consume chars if (string->length()) { char c = string->charAt(0); string->remove(0, 1); return c; } } else if (peekPointer < (int)string->length()) { // return pointed and move pointer return string->charAt(peekPointer++); } // everything is read return -1; } virtual size_t write(uint8_t data) override { return string->concat((char)data); } // virtual int read(uint8_t* buffer, size_t len) override // { // if (peekPointer < 0) // { // // string will be consumed // size_t l = std::min(len, (size_t)string->length()); // memcpy(buffer, string->c_str(), l); // string->remove(0, l); // return l; // } // // if (peekPointer >= (int)string->length()) // { // return 0; // } // // // only the pointer is moved // size_t l = std::min(len, (size_t)(string->length() - peekPointer)); // memcpy(buffer, string->c_str() + peekPointer, l); // peekPointer += l; // return l; // } virtual size_t write(const uint8_t* buffer, size_t len) override { return string->concat((const char*)buffer, len) ? len : 0; } virtual int peek() override { if (peekPointer < 0) { if (string->length()) { return string->charAt(0); } } else if (peekPointer < (int)string->length()) { return string->charAt(peekPointer); } return -1; } virtual void flush() override { // nothing to do } #if 0 virtual bool inputCanTimeout() override { return false; } virtual bool outputCanTimeout() override { return false; } //// Stream's peekBufferAPI virtual bool hasPeekBufferAPI() const override { return true; } virtual size_t peekAvailable() { if (peekPointer < 0) { return string->length(); } return string->length() - peekPointer; } virtual const char* peekBuffer() override { if (peekPointer < 0) { return string->c_str(); } if (peekPointer < (int)string->length()) { return string->c_str() + peekPointer; } return nullptr; } virtual void peekConsume(size_t consume) override { if (peekPointer < 0) { // string is really consumed string->remove(0, consume); } else { // only the pointer is moved peekPointer = std::min((size_t)string->length(), peekPointer + consume); } } virtual ssize_t streamRemaining() override { return peekPointer < 0 ? string->length() : string->length() - peekPointer; } // calling setConsume() will consume bytes as the stream is read // (enabled by default) void setConsume() { peekPointer = -1; } #endif // Reading this stream will mark the string as read without consuming // (not enabled by default) // Calling resetPointer() resets the read state and allows rereading. void resetPointer(int pointer = 0) { peekPointer = pointer; } protected: String* string; int peekPointer; // -1:String is consumed / >=0:resettable pointer }; // StreamString is a S2Stream holding the String class StreamString: public String, public S2Stream { protected: void resetpp() { if (peekPointer > 0) { peekPointer = 0; } } public: StreamString(StreamString&& bro) : String(bro), S2Stream(this) { } StreamString(const StreamString& bro) : String(bro), S2Stream(this) { } // duplicate String constructors and operator=: StreamString(const char* text = nullptr) : String(text), S2Stream(this) { } StreamString(const String& string) : String(string), S2Stream(this) { } StreamString(const __FlashStringHelper* str) : String(str), S2Stream(this) { } StreamString(String&& string) : String(string), S2Stream(this) { } explicit StreamString(char c) : String(c), S2Stream(this) { } explicit StreamString(unsigned char c, unsigned char base = 10) : String(c, base), S2Stream(this) { } explicit StreamString(int i, unsigned char base = 10) : String(i, base), S2Stream(this) { } explicit StreamString(unsigned int i, unsigned char base = 10) : String(i, base), S2Stream(this) { } explicit StreamString(long l, unsigned char base = 10) : String(l, base), S2Stream(this) { } explicit StreamString(unsigned long l, unsigned char base = 10) : String(l, base), S2Stream(this) { } explicit StreamString(float f, unsigned char decimalPlaces = 2) : String(f, decimalPlaces), S2Stream(this) { } explicit StreamString(double d, unsigned char decimalPlaces = 2) : String(d, decimalPlaces), S2Stream(this) { } StreamString& operator=(const StreamString& rhs) { String::operator=(rhs); resetpp(); return *this; } StreamString& operator=(const String& rhs) { String::operator=(rhs); resetpp(); return *this; } StreamString& operator=(const char* cstr) { String::operator=(cstr); resetpp(); return *this; } StreamString& operator=(const __FlashStringHelper* str) { String::operator=(str); resetpp(); return *this; } StreamString& operator=(String&& rval) { String::operator=(rval); resetpp(); return *this; } }; #endif // __STREAMSTRING_H