mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-16 00:43:00 +03:00
Stream::send() (#6979)
This commit is contained in:
@ -26,15 +26,15 @@
|
||||
class Client: public Stream {
|
||||
|
||||
public:
|
||||
virtual int connect(IPAddress ip, uint16_t port) =0;
|
||||
virtual int connect(const char *host, uint16_t port) =0;
|
||||
virtual size_t write(uint8_t) =0;
|
||||
virtual size_t write(const uint8_t *buf, size_t size) =0;
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual int connect(IPAddress ip, uint16_t port) = 0;
|
||||
virtual int connect(const char *host, uint16_t port) = 0;
|
||||
virtual size_t write(uint8_t) override = 0;
|
||||
virtual size_t write(const uint8_t *buf, size_t size) override = 0;
|
||||
virtual int available() override = 0;
|
||||
virtual int read() override = 0;
|
||||
virtual int read(uint8_t *buf, size_t size) override = 0;
|
||||
virtual int peek() override = 0;
|
||||
virtual void flush() override = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual uint8_t connected() = 0;
|
||||
virtual operator bool() = 0;
|
||||
|
@ -66,7 +66,7 @@ int File::read() {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t File::read(uint8_t* buf, size_t size) {
|
||||
int File::read(uint8_t* buf, size_t size) {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
|
@ -67,13 +67,14 @@ public:
|
||||
size_t readBytes(char *buffer, size_t length) override {
|
||||
return read((uint8_t*)buffer, length);
|
||||
}
|
||||
size_t read(uint8_t* buf, size_t size);
|
||||
int read(uint8_t* buf, size_t size) override;
|
||||
bool seek(uint32_t pos, SeekMode mode);
|
||||
bool seek(uint32_t pos) {
|
||||
return seek(pos, SeekSet);
|
||||
}
|
||||
size_t position() const;
|
||||
size_t size() const;
|
||||
virtual ssize_t streamRemaining() override { return (ssize_t)size() - (ssize_t)position(); }
|
||||
void close();
|
||||
operator bool() const;
|
||||
const char* name() const;
|
||||
@ -84,6 +85,7 @@ public:
|
||||
bool isDirectory() const;
|
||||
|
||||
// Arduino "class SD" methods for compatibility
|
||||
//TODO use stream::send / check read(buf,size) result
|
||||
template<typename T> size_t write(T &src){
|
||||
uint8_t obuf[256];
|
||||
size_t doneLen = 0;
|
||||
|
@ -30,7 +30,7 @@ class FileImpl {
|
||||
public:
|
||||
virtual ~FileImpl() { }
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
virtual size_t read(uint8_t* buf, size_t size) = 0;
|
||||
virtual int read(uint8_t* buf, size_t size) = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual bool seek(uint32_t pos, SeekMode mode) = 0;
|
||||
virtual size_t position() const = 0;
|
||||
|
@ -135,16 +135,45 @@ public:
|
||||
// return -1 when data is unvailable (arduino api)
|
||||
return uart_peek_char(_uart);
|
||||
}
|
||||
|
||||
virtual bool hasPeekBufferAPI () const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// return a pointer to available data buffer (size = available())
|
||||
// semantic forbids any kind of read() before calling peekConsume()
|
||||
const char* peekBuffer () override
|
||||
{
|
||||
return uart_peek_buffer(_uart);
|
||||
}
|
||||
|
||||
// return number of byte accessible by peekBuffer()
|
||||
size_t peekAvailable () override
|
||||
{
|
||||
return uart_peek_available(_uart);
|
||||
}
|
||||
|
||||
// consume bytes after use (see peekBuffer)
|
||||
void peekConsume (size_t consume) override
|
||||
{
|
||||
return uart_peek_consume(_uart, consume);
|
||||
}
|
||||
|
||||
int read(void) override
|
||||
{
|
||||
// return -1 when data is unvailable (arduino api)
|
||||
return uart_read_char(_uart);
|
||||
}
|
||||
// ::read(buffer, size): same as readBytes without timeout
|
||||
size_t read(char* buffer, size_t size)
|
||||
int read(char* buffer, size_t size)
|
||||
{
|
||||
return uart_read(_uart, buffer, size);
|
||||
}
|
||||
int read(uint8_t* buffer, size_t size) override
|
||||
{
|
||||
return uart_read(_uart, (char*)buffer, size);
|
||||
}
|
||||
size_t readBytes(char* buffer, size_t size) override;
|
||||
size_t readBytes(uint8_t* buffer, size_t size) override
|
||||
{
|
||||
|
@ -33,15 +33,7 @@
|
||||
|
||||
/* default implementation: may be overridden */
|
||||
size_t Print::write(const uint8_t *buffer, size_t size) {
|
||||
|
||||
#ifdef DEBUG_ESP_CORE
|
||||
static char not_the_best_way [] PROGMEM STORE_ATTR = "Print::write(data,len) should be overridden for better efficiency\r\n";
|
||||
static bool once = false;
|
||||
if (!once) {
|
||||
once = true;
|
||||
os_printf_plus(not_the_best_way);
|
||||
}
|
||||
#endif
|
||||
IAMSLOW();
|
||||
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
|
@ -111,6 +111,10 @@ class Print {
|
||||
size_t println(void);
|
||||
|
||||
virtual void flush() { /* Empty implementation for backward compatibility */ }
|
||||
|
||||
// by default write timeout is possible (outgoing data from network,serial..)
|
||||
// (children can override to false (like String))
|
||||
virtual bool outputCanTimeout () { return true; }
|
||||
};
|
||||
|
||||
template<> size_t Print::printNumber(double number, uint8_t digits);
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
|
||||
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
|
||||
|
||||
@ -210,6 +211,8 @@ float Stream::parseFloat(char skipChar) {
|
||||
// the buffer is NOT null terminated.
|
||||
//
|
||||
size_t Stream::readBytes(char *buffer, size_t length) {
|
||||
IAMSLOW();
|
||||
|
||||
size_t count = 0;
|
||||
while(count < length) {
|
||||
int c = timedRead();
|
||||
@ -258,3 +261,20 @@ String Stream::readStringUntil(char terminator) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// read what can be read, immediate exit on unavailable data
|
||||
// prototype similar to Arduino's `int Client::read(buf, len)`
|
||||
int Stream::read (uint8_t* buffer, size_t maxLen)
|
||||
{
|
||||
IAMSLOW();
|
||||
|
||||
size_t nbread = 0;
|
||||
while (nbread < maxLen && available())
|
||||
{
|
||||
int c = read();
|
||||
if (c == -1)
|
||||
break;
|
||||
buffer[nbread++] = read();
|
||||
}
|
||||
return nbread;
|
||||
}
|
||||
|
@ -22,10 +22,13 @@
|
||||
#ifndef Stream_h
|
||||
#define Stream_h
|
||||
|
||||
#include <debug.h>
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
#include <Print.h>
|
||||
#include <PolledTimeout.h>
|
||||
#include <sys/types.h> // ssize_t
|
||||
|
||||
// compatability macros for testing
|
||||
// compatibility macros for testing
|
||||
/*
|
||||
#define getInt() parseInt()
|
||||
#define getInt(skipChar) parseInt(skipchar)
|
||||
@ -35,6 +38,15 @@
|
||||
readBytesBetween( pre_string, terminator, buffer, length)
|
||||
*/
|
||||
|
||||
// Arduino `Client: public Stream` class defines `virtual int read(uint8_t *buf, size_t size) = 0;`
|
||||
// This function is now imported into `Stream::` for `Stream::send*()`.
|
||||
// Other classes inheriting from `Stream::` and implementing `read(uint8_t *buf, size_t size)`
|
||||
// must consequently use `int` as return type, namely Hardware/SoftwareSerial, FileSystems...
|
||||
#define STREAM_READ_RETURNS_INT 1
|
||||
|
||||
// Stream::send API is present
|
||||
#define STREAMSEND_API 1
|
||||
|
||||
class Stream: public Print {
|
||||
protected:
|
||||
unsigned long _timeout = 1000; // number of milliseconds to wait for the next char before aborting timed read
|
||||
@ -53,6 +65,7 @@ class Stream: public Print {
|
||||
// parsing methods
|
||||
|
||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||
unsigned long getTimeout () const { return _timeout; }
|
||||
|
||||
bool find(const char *target); // reads data from the stream until the target string is found
|
||||
bool find(uint8_t *target) {
|
||||
@ -102,12 +115,114 @@ class Stream: public Print {
|
||||
virtual String readString();
|
||||
String readStringUntil(char terminator);
|
||||
|
||||
virtual int read (uint8_t* buffer, size_t len);
|
||||
int read (char* buffer, size_t len) { return read((uint8_t*)buffer, len); }
|
||||
|
||||
//////////////////// extension: direct access to input buffer
|
||||
// to provide when possible a pointer to available data for read
|
||||
|
||||
// informs user and ::to*() on effective buffered peek API implementation
|
||||
// by default: not available
|
||||
virtual bool hasPeekBufferAPI () const { return false; }
|
||||
|
||||
// returns number of byte accessible by peekBuffer()
|
||||
virtual size_t peekAvailable () { return 0; }
|
||||
|
||||
// returns a pointer to available data buffer (size = peekAvailable())
|
||||
// semantic forbids any kind of ::read()
|
||||
// - after calling peekBuffer()
|
||||
// - and before calling peekConsume()
|
||||
virtual const char* peekBuffer () { return nullptr; }
|
||||
|
||||
// consumes bytes after peekBuffer() use
|
||||
// (then ::read() is allowed)
|
||||
virtual void peekConsume (size_t consume) { (void)consume; }
|
||||
|
||||
// by default read timeout is possible (incoming data from network,serial..)
|
||||
// children can override to false (like String::)
|
||||
virtual bool inputCanTimeout () { return true; }
|
||||
|
||||
// (outputCanTimeout() is defined in Print::)
|
||||
|
||||
////////////////////////
|
||||
//////////////////// extensions: Streaming streams to streams
|
||||
// Stream::send*()
|
||||
//
|
||||
// Stream::send*() uses 1-copy transfers when peekBuffer API is
|
||||
// available, or makes a regular transfer through a temporary buffer.
|
||||
//
|
||||
// - for efficiency, Stream classes should implement peekAPI when
|
||||
// possible
|
||||
// - for an efficient timeout management, Print/Stream classes
|
||||
// should implement {output,input}CanTimeout()
|
||||
|
||||
using oneShotMs = esp8266::polledTimeout::oneShotFastMs;
|
||||
static constexpr int temporaryStackBufferSize = 64;
|
||||
|
||||
// ::send*() methods:
|
||||
// - always stop before timeout when "no-more-input-possible-data"
|
||||
// or "no-more-output-possible-data" condition is met
|
||||
// - always return number of transfered bytes
|
||||
// When result is 0 or less than requested maxLen, Print::getLastSend()
|
||||
// contains an error reason.
|
||||
|
||||
// transfers already buffered / immediately available data (no timeout)
|
||||
// returns number of transfered bytes
|
||||
size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
|
||||
size_t sendAvailable (Print& to) { return sendAvailable(&to); }
|
||||
|
||||
// transfers data until timeout
|
||||
// returns number of transfered bytes
|
||||
size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
|
||||
size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }
|
||||
|
||||
// transfers data until a char is encountered (the char is swallowed but not transfered) with timeout
|
||||
// returns number of transfered bytes
|
||||
size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
|
||||
size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }
|
||||
|
||||
// transfers data until requested size or timeout
|
||||
// returns number of transfered bytes
|
||||
size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
|
||||
size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }
|
||||
|
||||
// remaining size (-1 by default = unknown)
|
||||
virtual ssize_t streamRemaining () { return -1; }
|
||||
|
||||
enum class Report
|
||||
{
|
||||
Success = 0,
|
||||
TimedOut,
|
||||
ReadError,
|
||||
WriteError,
|
||||
ShortOperation,
|
||||
};
|
||||
|
||||
Report getLastSendReport () const { return _sendReport; }
|
||||
|
||||
protected:
|
||||
long parseInt(char skipChar); // as above but the given skipChar is ignored
|
||||
// as above but the given skipChar is ignored
|
||||
size_t sendGeneric (Print* to,
|
||||
const ssize_t len = -1,
|
||||
const int readUntilChar = -1,
|
||||
oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */);
|
||||
|
||||
size_t SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs);
|
||||
size_t SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs);
|
||||
size_t SendGenericRegular(Print* to, const ssize_t len, const oneShotMs::timeType timeoutMs);
|
||||
|
||||
void setReport (Report report) { _sendReport = report; }
|
||||
|
||||
private:
|
||||
|
||||
Report _sendReport = Report::Success;
|
||||
|
||||
//////////////////// end of extensions
|
||||
|
||||
protected:
|
||||
long parseInt(char skipChar); // as parseInt() but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
|
||||
float parseFloat(char skipChar); // as above but the given skipChar is ignored
|
||||
float parseFloat(char skipChar); // as parseFloat() but the given skipChar is ignored
|
||||
};
|
||||
|
||||
#endif
|
||||
|
248
cores/esp8266/StreamDev.h
Normal file
248
cores/esp8266/StreamDev.h
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
StreamDev.h - Stream helpers
|
||||
Copyright (c) 2019 David Gauchard. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __STREAMDEV_H
|
||||
#define __STREAMDEV_H
|
||||
|
||||
#include <limits>
|
||||
#include <esp_priv.h>
|
||||
#include <StreamString.h>
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// /dev/null
|
||||
// - black hole as output, swallow everything, availableForWrite = infinite
|
||||
// - black hole as input, nothing to read, available = 0
|
||||
|
||||
class StreamNull: public Stream
|
||||
{
|
||||
public:
|
||||
|
||||
// Print
|
||||
virtual size_t write(uint8_t) override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t* buffer, size_t size) override
|
||||
{
|
||||
(void)buffer;
|
||||
(void)size;
|
||||
return size;
|
||||
}
|
||||
|
||||
virtual int availableForWrite() override
|
||||
{
|
||||
return std::numeric_limits<int16_t>::max();
|
||||
}
|
||||
|
||||
// Stream
|
||||
virtual int available() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int read() override
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual int peek() override
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual size_t readBytes(char* buffer, size_t len) override
|
||||
{
|
||||
(void)buffer;
|
||||
(void)len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int read(uint8_t* buffer, size_t len) override
|
||||
{
|
||||
(void)buffer;
|
||||
(void)len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool outputCanTimeout() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool inputCanTimeout() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ssize_t streamRemaining() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// /dev/zero
|
||||
// - black hole as output, swallow everything, availableForWrite = infinite
|
||||
// - big bang as input, gives infinity to read, available = infinite
|
||||
|
||||
class StreamZero: public StreamNull
|
||||
{
|
||||
protected:
|
||||
|
||||
char _zero;
|
||||
|
||||
public:
|
||||
|
||||
StreamZero(char zero = 0): _zero(zero) { }
|
||||
|
||||
// Stream
|
||||
virtual int available() override
|
||||
{
|
||||
return std::numeric_limits<int16_t>::max();
|
||||
}
|
||||
|
||||
virtual int read() override
|
||||
{
|
||||
return _zero;
|
||||
}
|
||||
|
||||
virtual int peek() override
|
||||
{
|
||||
return _zero;
|
||||
}
|
||||
|
||||
virtual size_t readBytes(char* buffer, size_t len) override
|
||||
{
|
||||
memset(buffer, _zero, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
virtual int read(uint8_t* buffer, size_t len) override
|
||||
{
|
||||
memset((char*)buffer, _zero, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
virtual ssize_t streamRemaining() override
|
||||
{
|
||||
return std::numeric_limits<int16_t>::max();
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// static buffer (in flash or ram)
|
||||
// - black hole as output, swallow everything, availableForWrite = infinite
|
||||
// - Stream buffer out as input, resettable
|
||||
|
||||
class StreamConstPtr: public StreamNull
|
||||
{
|
||||
protected:
|
||||
const char* _buffer;
|
||||
size_t _size;
|
||||
bool _byteAddressable;
|
||||
size_t _peekPointer = 0;
|
||||
|
||||
public:
|
||||
StreamConstPtr(const String& string): _buffer(string.c_str()), _size(string.length()), _byteAddressable(true) { }
|
||||
StreamConstPtr(const char* buffer, size_t size): _buffer(buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { }
|
||||
StreamConstPtr(const uint8_t* buffer, size_t size): _buffer((const char*)buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { }
|
||||
StreamConstPtr(const __FlashStringHelper* buffer, size_t size): _buffer(reinterpret_cast<const char*>(buffer)), _size(size), _byteAddressable(false) { }
|
||||
StreamConstPtr(const __FlashStringHelper* text): _buffer(reinterpret_cast<const char*>(text)), _size(strlen_P((PGM_P)text)), _byteAddressable(false) { }
|
||||
|
||||
void resetPointer(int pointer = 0)
|
||||
{
|
||||
_peekPointer = pointer;
|
||||
}
|
||||
|
||||
// Stream
|
||||
virtual int available() override
|
||||
{
|
||||
return peekAvailable();
|
||||
}
|
||||
|
||||
virtual int read() override
|
||||
{
|
||||
return _peekPointer < _size ? _buffer[_peekPointer++] : -1;
|
||||
}
|
||||
|
||||
virtual int peek() override
|
||||
{
|
||||
return _peekPointer < _size ? _buffer[_peekPointer] : -1;
|
||||
}
|
||||
|
||||
virtual size_t readBytes(char* buffer, size_t len) override
|
||||
{
|
||||
if (_peekPointer >= _size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
size_t cpylen = std::min(_size - _peekPointer, len);
|
||||
memcpy_P(buffer, _buffer + _peekPointer, cpylen); // whether byte adressible is true
|
||||
_peekPointer += cpylen;
|
||||
return cpylen;
|
||||
}
|
||||
|
||||
virtual int read(uint8_t* buffer, size_t len) override
|
||||
{
|
||||
return readBytes((char*)buffer, len);
|
||||
}
|
||||
|
||||
virtual ssize_t streamRemaining() override
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
// peekBuffer
|
||||
virtual bool hasPeekBufferAPI() const override
|
||||
{
|
||||
return _byteAddressable;
|
||||
}
|
||||
|
||||
virtual size_t peekAvailable() override
|
||||
{
|
||||
return _peekPointer < _size ? _size - _peekPointer : 0;
|
||||
}
|
||||
|
||||
virtual const char* peekBuffer() override
|
||||
{
|
||||
return _peekPointer < _size ? _buffer + _peekPointer : nullptr;
|
||||
}
|
||||
|
||||
virtual void peekConsume(size_t consume) override
|
||||
{
|
||||
_peekPointer += consume;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////
|
||||
|
||||
Stream& operator << (Stream& out, String& string);
|
||||
Stream& operator << (Stream& out, Stream& stream);
|
||||
Stream& operator << (Stream& out, StreamString& stream);
|
||||
Stream& operator << (Stream& out, const char* text);
|
||||
Stream& operator << (Stream& out, const __FlashStringHelper* text);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_STREAMDEV)
|
||||
extern StreamNull devnull;
|
||||
#endif
|
||||
|
||||
#endif // __STREAMDEV_H
|
383
cores/esp8266/StreamSend.cpp
Normal file
383
cores/esp8266/StreamSend.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
/*
|
||||
StreamDev.cpp - 1-copy transfer related methods
|
||||
Copyright (c) 2019 David Gauchard. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <StreamDev.h>
|
||||
|
||||
size_t Stream::sendGeneric(Print* to,
|
||||
const ssize_t len,
|
||||
const int readUntilChar,
|
||||
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||
{
|
||||
setReport(Report::Success);
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
return 0; // conveniently avoids timeout for no requested data
|
||||
}
|
||||
|
||||
// There are two timeouts:
|
||||
// - read (network, serial, ...)
|
||||
// - write (network, serial, ...)
|
||||
// However
|
||||
// - getTimeout() is for reading only
|
||||
// - there is no getOutputTimeout() api
|
||||
// So we use getTimeout() for both,
|
||||
// (also when inputCanTimeout() is false)
|
||||
|
||||
if (hasPeekBufferAPI())
|
||||
{
|
||||
return SendGenericPeekBuffer(to, len, readUntilChar, timeoutMs);
|
||||
}
|
||||
|
||||
if (readUntilChar >= 0)
|
||||
{
|
||||
return SendGenericRegularUntil(to, len, readUntilChar, timeoutMs);
|
||||
}
|
||||
|
||||
return SendGenericRegular(to, len, timeoutMs);
|
||||
}
|
||||
|
||||
|
||||
size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||
{
|
||||
// "neverExpires (default, impossible)" is translated to default timeout
|
||||
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs);
|
||||
// len==-1 => maxLen=0 <=> until starvation
|
||||
const size_t maxLen = std::max((ssize_t)0, len);
|
||||
size_t written = 0;
|
||||
|
||||
while (!maxLen || written < maxLen)
|
||||
{
|
||||
size_t avpk = peekAvailable();
|
||||
if (avpk == 0 && !inputCanTimeout())
|
||||
{
|
||||
// no more data to read, ever
|
||||
break;
|
||||
}
|
||||
|
||||
size_t w = to->availableForWrite();
|
||||
if (w == 0 && !to->outputCanTimeout())
|
||||
{
|
||||
// no more data can be written, ever
|
||||
break;
|
||||
}
|
||||
|
||||
w = std::min(w, avpk);
|
||||
if (maxLen)
|
||||
{
|
||||
w = std::min(w, maxLen - written);
|
||||
}
|
||||
if (w)
|
||||
{
|
||||
const char* directbuf = peekBuffer();
|
||||
bool foundChar = false;
|
||||
if (readUntilChar >= 0)
|
||||
{
|
||||
const char* last = (const char*)memchr(directbuf, readUntilChar, w);
|
||||
if (last)
|
||||
{
|
||||
w = std::min((size_t)(last - directbuf), w);
|
||||
foundChar = true;
|
||||
}
|
||||
}
|
||||
if (w && ((w = to->write(directbuf, w))))
|
||||
{
|
||||
peekConsume(w);
|
||||
written += w;
|
||||
if (maxLen)
|
||||
{
|
||||
timedOut.reset();
|
||||
}
|
||||
}
|
||||
if (foundChar)
|
||||
{
|
||||
peekConsume(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!w && !maxLen && readUntilChar < 0)
|
||||
{
|
||||
// nothing has been transferred and no specific condition is requested
|
||||
break;
|
||||
}
|
||||
|
||||
if (timedOut)
|
||||
{
|
||||
// either (maxLen>0) nothing has been transferred for too long
|
||||
// or readUntilChar >= 0 but char is not encountered for too long
|
||||
// or (maxLen=0) too much time has been spent here
|
||||
break;
|
||||
}
|
||||
|
||||
optimistic_yield(1000);
|
||||
}
|
||||
|
||||
if (getLastSendReport() == Report::Success && maxLen > 0)
|
||||
{
|
||||
if (timeoutMs && timedOut)
|
||||
{
|
||||
setReport(Report::TimedOut);
|
||||
}
|
||||
else if ((ssize_t)written != len)
|
||||
{
|
||||
// This is happening when source cannot timeout (ex: a String)
|
||||
// but has not enough data, or a dest has closed or cannot
|
||||
// timeout but is too small (String, buffer...)
|
||||
//
|
||||
// Mark it as an error because user usually wants to get what is
|
||||
// asked for.
|
||||
setReport(Report::ShortOperation);
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||
{
|
||||
// regular Stream API
|
||||
// no other choice than reading byte by byte
|
||||
|
||||
// "neverExpires (default, impossible)" is translated to default timeout
|
||||
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs);
|
||||
// len==-1 => maxLen=0 <=> until starvation
|
||||
const size_t maxLen = std::max((ssize_t)0, len);
|
||||
size_t written = 0;
|
||||
|
||||
while (!maxLen || written < maxLen)
|
||||
{
|
||||
size_t avr = available();
|
||||
if (avr == 0 && !inputCanTimeout())
|
||||
{
|
||||
// no more data to read, ever
|
||||
break;
|
||||
}
|
||||
|
||||
size_t w = to->availableForWrite();
|
||||
if (w == 0 && !to->outputCanTimeout())
|
||||
{
|
||||
// no more data can be written, ever
|
||||
break;
|
||||
}
|
||||
|
||||
int c = read();
|
||||
if (c != -1)
|
||||
{
|
||||
if (c == readUntilChar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
w = to->write(c);
|
||||
if (w != 1)
|
||||
{
|
||||
setReport(Report::WriteError);
|
||||
break;
|
||||
}
|
||||
written += 1;
|
||||
if (maxLen)
|
||||
{
|
||||
timedOut.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (!w && !maxLen && readUntilChar < 0)
|
||||
{
|
||||
// nothing has been transferred and no specific condition is requested
|
||||
break;
|
||||
}
|
||||
|
||||
if (timedOut)
|
||||
{
|
||||
// either (maxLen>0) nothing has been transferred for too long
|
||||
// or readUntilChar >= 0 but char is not encountered for too long
|
||||
// or (maxLen=0) too much time has been spent here
|
||||
break;
|
||||
}
|
||||
|
||||
optimistic_yield(1000);
|
||||
}
|
||||
|
||||
if (getLastSendReport() == Report::Success && maxLen > 0)
|
||||
{
|
||||
if (timeoutMs && timedOut)
|
||||
{
|
||||
setReport(Report::TimedOut);
|
||||
}
|
||||
else if ((ssize_t)written != len)
|
||||
{
|
||||
// This is happening when source cannot timeout (ex: a String)
|
||||
// but has not enough data, or a dest has closed or cannot
|
||||
// timeout but is too small (String, buffer...)
|
||||
//
|
||||
// Mark it as an error because user usually wants to get what is
|
||||
// asked for.
|
||||
setReport(Report::ShortOperation);
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||
{
|
||||
// regular Stream API
|
||||
// use an intermediary buffer
|
||||
|
||||
// "neverExpires (default, impossible)" is translated to default timeout
|
||||
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs);
|
||||
// len==-1 => maxLen=0 <=> until starvation
|
||||
const size_t maxLen = std::max((ssize_t)0, len);
|
||||
size_t written = 0;
|
||||
|
||||
while (!maxLen || written < maxLen)
|
||||
{
|
||||
size_t avr = available();
|
||||
if (avr == 0 && !inputCanTimeout())
|
||||
{
|
||||
// no more data to read, ever
|
||||
break;
|
||||
}
|
||||
|
||||
size_t w = to->availableForWrite();
|
||||
if (w == 0 && !to->outputCanTimeout())
|
||||
// no more data can be written, ever
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
w = std::min(w, avr);
|
||||
if (maxLen)
|
||||
{
|
||||
w = std::min(w, maxLen - written);
|
||||
}
|
||||
w = std::min(w, (decltype(w))temporaryStackBufferSize);
|
||||
if (w)
|
||||
{
|
||||
char temp[w];
|
||||
ssize_t r = read(temp, w);
|
||||
if (r < 0)
|
||||
{
|
||||
setReport(Report::ReadError);
|
||||
break;
|
||||
}
|
||||
w = to->write(temp, r);
|
||||
written += w;
|
||||
if ((size_t)r != w)
|
||||
{
|
||||
setReport(Report::WriteError);
|
||||
break;
|
||||
}
|
||||
if (maxLen && w)
|
||||
{
|
||||
timedOut.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (!w && !maxLen)
|
||||
{
|
||||
// nothing has been transferred and no specific condition is requested
|
||||
break;
|
||||
}
|
||||
|
||||
if (timedOut)
|
||||
{
|
||||
// either (maxLen>0) nothing has been transferred for too long
|
||||
// or readUntilChar >= 0 but char is not encountered for too long
|
||||
// or (maxLen=0) too much time has been spent here
|
||||
break;
|
||||
}
|
||||
|
||||
optimistic_yield(1000);
|
||||
}
|
||||
|
||||
if (getLastSendReport() == Report::Success && maxLen > 0)
|
||||
{
|
||||
if (timeoutMs && timedOut)
|
||||
{
|
||||
setReport(Report::TimedOut);
|
||||
}
|
||||
else if ((ssize_t)written != len)
|
||||
{
|
||||
// This is happening when source cannot timeout (ex: a String)
|
||||
// but has not enough data, or a dest has closed or cannot
|
||||
// timeout but is too small (String, buffer...)
|
||||
//
|
||||
// Mark it as an error because user usually wants to get what is
|
||||
// asked for.
|
||||
setReport(Report::ShortOperation);
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
Stream& operator << (Stream& out, String& string)
|
||||
{
|
||||
StreamConstPtr(string).sendAll(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
Stream& operator << (Stream& out, StreamString& stream)
|
||||
{
|
||||
stream.sendAll(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
Stream& operator << (Stream& out, Stream& stream)
|
||||
{
|
||||
if (stream.streamRemaining() < 0)
|
||||
{
|
||||
if (stream.inputCanTimeout())
|
||||
{
|
||||
// restrict with only what's buffered on input
|
||||
stream.sendAvailable(out);
|
||||
}
|
||||
else
|
||||
{
|
||||
// take all what is in input
|
||||
stream.sendAll(out);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.sendSize(out, stream.streamRemaining());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Stream& operator << (Stream& out, const char* text)
|
||||
{
|
||||
StreamConstPtr(text).sendAll(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
Stream& operator << (Stream& out, const __FlashStringHelper* text)
|
||||
{
|
||||
StreamConstPtr(text).sendAll(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_STREAMDEV)
|
||||
StreamNull devnull;
|
||||
#endif
|
@ -1,68 +0,0 @@
|
||||
/**
|
||||
StreamString.cpp
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. 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
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "StreamString.h"
|
||||
|
||||
size_t StreamString::write(const uint8_t *data, size_t size) {
|
||||
if(size && data) {
|
||||
const unsigned int newlen = length() + size;
|
||||
if(reserve(newlen + 1)) {
|
||||
memcpy((void *) (wbuffer() + len()), (const void *) data, size);
|
||||
setLen(newlen);
|
||||
*(wbuffer() + newlen) = 0x00; // add null for string end
|
||||
return size;
|
||||
}
|
||||
DEBUGV(":stream2string: OOM (%d->%d)\n", length(), newlen+1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t StreamString::write(uint8_t data) {
|
||||
return concat((char) data);
|
||||
}
|
||||
|
||||
int StreamString::available() {
|
||||
return length();
|
||||
}
|
||||
|
||||
int StreamString::read() {
|
||||
if(length()) {
|
||||
char c = charAt(0);
|
||||
remove(0, 1);
|
||||
return c;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int StreamString::peek() {
|
||||
if(length()) {
|
||||
char c = charAt(0);
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void StreamString::flush() {
|
||||
}
|
||||
|
@ -1,39 +1,293 @@
|
||||
/**
|
||||
StreamString.h
|
||||
StreamString.h
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
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 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.
|
||||
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
|
||||
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_
|
||||
#ifndef __STREAMSTRING_H
|
||||
#define __STREAMSTRING_H
|
||||
|
||||
#include <limits>
|
||||
#include "WString.h"
|
||||
|
||||
class StreamString: public Stream, public String {
|
||||
///////////////////////////////////////////////////////////////
|
||||
// S2Stream points to a String and makes it a Stream
|
||||
// (it is also the helper for StreamString)
|
||||
|
||||
class S2Stream: public Stream
|
||||
{
|
||||
public:
|
||||
size_t write(const uint8_t *buffer, size_t size) override;
|
||||
size_t write(uint8_t data) override;
|
||||
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
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<int16_t>::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
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
#endif /* STREAMSTRING_H_ */
|
||||
// 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 contructors 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) { }
|
||||
StreamString(StringSumHelper&& sum): String(sum), 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;
|
||||
}
|
||||
|
||||
StreamString& operator= (StringSumHelper&& rval)
|
||||
{
|
||||
String::operator=(rval);
|
||||
resetpp();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __STREAMSTRING_H
|
||||
|
@ -22,6 +22,13 @@
|
||||
#include "debug.h"
|
||||
#include "osapi.h"
|
||||
|
||||
#ifdef DEBUG_ESP_CORE
|
||||
void __iamslow(const char* what)
|
||||
{
|
||||
DEBUGV("%s should be overridden for better efficiency\r\n", what);
|
||||
}
|
||||
#endif
|
||||
|
||||
IRAM_ATTR
|
||||
void hexdump(const void *mem, uint32_t len, uint8_t cols)
|
||||
{
|
||||
|
@ -26,6 +26,20 @@ void __unhandled_exception(const char *str) __attribute__((noreturn));
|
||||
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
|
||||
#define panic() __panic_func(PSTR(__FILE__), __LINE__, __func__)
|
||||
|
||||
#ifdef DEBUG_ESP_CORE
|
||||
extern void __iamslow(const char* what);
|
||||
#define IAMSLOW() \
|
||||
do { \
|
||||
static bool once = false; \
|
||||
if (!once) { \
|
||||
once = true; \
|
||||
__iamslow((PGM_P)FPSTR(__FUNCTION__)); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define IAMSLOW() do { (void)0; } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
44
cores/esp8266/esp_priv.h
Normal file
44
cores/esp8266/esp_priv.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
esp_priv.h - private esp8266 helpers
|
||||
Copyright (c) 2020 esp8266/Arduino community. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __ESP_PRIV
|
||||
#define __ESP_PRIV
|
||||
|
||||
#if defined(CORE_MOCK)
|
||||
|
||||
constexpr bool __byteAddressable(const void* addr)
|
||||
{
|
||||
(void)addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else // on hardware
|
||||
|
||||
#include <sys/config.h>
|
||||
|
||||
// returns true when addr can be used without "pgm_" functions or non32xfer service
|
||||
constexpr bool __byteAddressable(const void* addr)
|
||||
{
|
||||
return addr < (const void*)(XCHAL_DATARAM0_VADDR + XCHAL_DATARAM0_SIZE);
|
||||
}
|
||||
|
||||
#endif // on hardware
|
||||
|
||||
#endif // __ESP_PRIV
|
@ -388,10 +388,10 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t read(uint8_t* buf, size_t size) override
|
||||
int read(uint8_t* buf, size_t size) override
|
||||
{
|
||||
CHECKFD();
|
||||
auto result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size);
|
||||
int result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size);
|
||||
if (result < 0) {
|
||||
DEBUGV("SPIFFS_read rc=%d\r\n", result);
|
||||
return 0;
|
||||
|
@ -240,6 +240,41 @@ uart_peek_char(uart_t* uart)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return number of byte accessible by uart_peek_buffer()
|
||||
size_t uart_peek_available (uart_t* uart)
|
||||
{
|
||||
// path for further optimization:
|
||||
// - return already copied buffer pointer (= older data)
|
||||
// - or return fifo when buffer is empty but then any move from fifo to
|
||||
// buffer should be blocked until peek_consume is called
|
||||
|
||||
ETS_UART_INTR_DISABLE();
|
||||
uart_rx_copy_fifo_to_buffer_unsafe(uart);
|
||||
auto rpos = uart->rx_buffer->rpos;
|
||||
auto wpos = uart->rx_buffer->wpos;
|
||||
ETS_UART_INTR_ENABLE();
|
||||
if(wpos < rpos)
|
||||
return uart->rx_buffer->size - rpos;
|
||||
return wpos - rpos;
|
||||
}
|
||||
|
||||
// return a pointer to available data buffer (size = available())
|
||||
// semantic forbids any kind of read() between peekBuffer() and peekConsume()
|
||||
const char* uart_peek_buffer (uart_t* uart)
|
||||
{
|
||||
return (const char*)&uart->rx_buffer->buffer[uart->rx_buffer->rpos];
|
||||
}
|
||||
|
||||
// consume bytes after use (see uart_peek_buffer)
|
||||
void uart_peek_consume (uart_t* uart, size_t consume)
|
||||
{
|
||||
ETS_UART_INTR_DISABLE();
|
||||
uart->rx_buffer->rpos += consume;
|
||||
if (uart->rx_buffer->rpos >= uart->rx_buffer->size)
|
||||
uart->rx_buffer->rpos -= uart->rx_buffer->size;
|
||||
ETS_UART_INTR_ENABLE();
|
||||
}
|
||||
|
||||
int
|
||||
uart_read_char(uart_t* uart)
|
||||
{
|
||||
|
@ -147,6 +147,16 @@ int uart_get_debug();
|
||||
void uart_start_detect_baudrate(int uart_nr);
|
||||
int uart_detect_baudrate(int uart_nr);
|
||||
|
||||
// return number of byte accessible by peekBuffer()
|
||||
size_t uart_peek_available (uart_t* uart);
|
||||
|
||||
// return a pointer to available data buffer (size = available())
|
||||
// semantic forbids any kind of read() before calling peekConsume()
|
||||
const char* uart_peek_buffer (uart_t* uart);
|
||||
|
||||
// consume bytes after use (see peekBuffer)
|
||||
void uart_peek_consume (uart_t* uart, size_t consume);
|
||||
|
||||
uint8_t uart_get_bit_length(const int uart_nr);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
|
Reference in New Issue
Block a user