Skip to content

Commit c720c0d

Browse files
authored
Stream::send() (#6979)
1 parent 4cc1472 commit c720c0d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2128
-642
lines changed

cores/esp8266/Client.h

+9-9
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
class Client: public Stream {
2727

2828
public:
29-
virtual int connect(IPAddress ip, uint16_t port) =0;
30-
virtual int connect(const char *host, uint16_t port) =0;
31-
virtual size_t write(uint8_t) =0;
32-
virtual size_t write(const uint8_t *buf, size_t size) =0;
33-
virtual int available() = 0;
34-
virtual int read() = 0;
35-
virtual int read(uint8_t *buf, size_t size) = 0;
36-
virtual int peek() = 0;
37-
virtual void flush() = 0;
29+
virtual int connect(IPAddress ip, uint16_t port) = 0;
30+
virtual int connect(const char *host, uint16_t port) = 0;
31+
virtual size_t write(uint8_t) override = 0;
32+
virtual size_t write(const uint8_t *buf, size_t size) override = 0;
33+
virtual int available() override = 0;
34+
virtual int read() override = 0;
35+
virtual int read(uint8_t *buf, size_t size) override = 0;
36+
virtual int peek() override = 0;
37+
virtual void flush() override = 0;
3838
virtual void stop() = 0;
3939
virtual uint8_t connected() = 0;
4040
virtual operator bool() = 0;

cores/esp8266/FS.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ int File::read() {
6666
return result;
6767
}
6868

69-
size_t File::read(uint8_t* buf, size_t size) {
69+
int File::read(uint8_t* buf, size_t size) {
7070
if (!_p)
7171
return 0;
7272

cores/esp8266/FS.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,14 @@ class File : public Stream
6767
size_t readBytes(char *buffer, size_t length) override {
6868
return read((uint8_t*)buffer, length);
6969
}
70-
size_t read(uint8_t* buf, size_t size);
70+
int read(uint8_t* buf, size_t size) override;
7171
bool seek(uint32_t pos, SeekMode mode);
7272
bool seek(uint32_t pos) {
7373
return seek(pos, SeekSet);
7474
}
7575
size_t position() const;
7676
size_t size() const;
77+
virtual ssize_t streamRemaining() override { return (ssize_t)size() - (ssize_t)position(); }
7778
void close();
7879
operator bool() const;
7980
const char* name() const;
@@ -84,6 +85,7 @@ class File : public Stream
8485
bool isDirectory() const;
8586

8687
// Arduino "class SD" methods for compatibility
88+
//TODO use stream::send / check read(buf,size) result
8789
template<typename T> size_t write(T &src){
8890
uint8_t obuf[256];
8991
size_t doneLen = 0;

cores/esp8266/FSImpl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class FileImpl {
3030
public:
3131
virtual ~FileImpl() { }
3232
virtual size_t write(const uint8_t *buf, size_t size) = 0;
33-
virtual size_t read(uint8_t* buf, size_t size) = 0;
33+
virtual int read(uint8_t* buf, size_t size) = 0;
3434
virtual void flush() = 0;
3535
virtual bool seek(uint32_t pos, SeekMode mode) = 0;
3636
virtual size_t position() const = 0;

cores/esp8266/HardwareSerial.h

+30-1
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,45 @@ class HardwareSerial: public Stream
135135
// return -1 when data is unvailable (arduino api)
136136
return uart_peek_char(_uart);
137137
}
138+
139+
virtual bool hasPeekBufferAPI () const override
140+
{
141+
return true;
142+
}
143+
144+
// return a pointer to available data buffer (size = available())
145+
// semantic forbids any kind of read() before calling peekConsume()
146+
const char* peekBuffer () override
147+
{
148+
return uart_peek_buffer(_uart);
149+
}
150+
151+
// return number of byte accessible by peekBuffer()
152+
size_t peekAvailable () override
153+
{
154+
return uart_peek_available(_uart);
155+
}
156+
157+
// consume bytes after use (see peekBuffer)
158+
void peekConsume (size_t consume) override
159+
{
160+
return uart_peek_consume(_uart, consume);
161+
}
162+
138163
int read(void) override
139164
{
140165
// return -1 when data is unvailable (arduino api)
141166
return uart_read_char(_uart);
142167
}
143168
// ::read(buffer, size): same as readBytes without timeout
144-
size_t read(char* buffer, size_t size)
169+
int read(char* buffer, size_t size)
145170
{
146171
return uart_read(_uart, buffer, size);
147172
}
173+
int read(uint8_t* buffer, size_t size) override
174+
{
175+
return uart_read(_uart, (char*)buffer, size);
176+
}
148177
size_t readBytes(char* buffer, size_t size) override;
149178
size_t readBytes(uint8_t* buffer, size_t size) override
150179
{

cores/esp8266/Print.cpp

+1-9
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,7 @@
3333

3434
/* default implementation: may be overridden */
3535
size_t Print::write(const uint8_t *buffer, size_t size) {
36-
37-
#ifdef DEBUG_ESP_CORE
38-
static char not_the_best_way [] PROGMEM STORE_ATTR = "Print::write(data,len) should be overridden for better efficiency\r\n";
39-
static bool once = false;
40-
if (!once) {
41-
once = true;
42-
os_printf_plus(not_the_best_way);
43-
}
44-
#endif
36+
IAMSLOW();
4537

4638
size_t n = 0;
4739
while (size--) {

cores/esp8266/Print.h

+4
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class Print {
111111
size_t println(void);
112112

113113
virtual void flush() { /* Empty implementation for backward compatibility */ }
114+
115+
// by default write timeout is possible (outgoing data from network,serial..)
116+
// (children can override to false (like String))
117+
virtual bool outputCanTimeout () { return true; }
114118
};
115119

116120
template<> size_t Print::printNumber(double number, uint8_t digits);

cores/esp8266/Stream.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <Arduino.h>
2424
#include <Stream.h>
25+
2526
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
2627
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
2728

@@ -210,6 +211,8 @@ float Stream::parseFloat(char skipChar) {
210211
// the buffer is NOT null terminated.
211212
//
212213
size_t Stream::readBytes(char *buffer, size_t length) {
214+
IAMSLOW();
215+
213216
size_t count = 0;
214217
while(count < length) {
215218
int c = timedRead();
@@ -258,3 +261,20 @@ String Stream::readStringUntil(char terminator) {
258261
}
259262
return ret;
260263
}
264+
265+
// read what can be read, immediate exit on unavailable data
266+
// prototype similar to Arduino's `int Client::read(buf, len)`
267+
int Stream::read (uint8_t* buffer, size_t maxLen)
268+
{
269+
IAMSLOW();
270+
271+
size_t nbread = 0;
272+
while (nbread < maxLen && available())
273+
{
274+
int c = read();
275+
if (c == -1)
276+
break;
277+
buffer[nbread++] = read();
278+
}
279+
return nbread;
280+
}

cores/esp8266/Stream.h

+120-5
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
#ifndef Stream_h
2323
#define Stream_h
2424

25+
#include <debug.h>
2526
#include <inttypes.h>
26-
#include "Print.h"
27+
#include <Print.h>
28+
#include <PolledTimeout.h>
29+
#include <sys/types.h> // ssize_t
2730

28-
// compatability macros for testing
31+
// compatibility macros for testing
2932
/*
3033
#define getInt() parseInt()
3134
#define getInt(skipChar) parseInt(skipchar)
@@ -35,6 +38,15 @@
3538
readBytesBetween( pre_string, terminator, buffer, length)
3639
*/
3740

41+
// Arduino `Client: public Stream` class defines `virtual int read(uint8_t *buf, size_t size) = 0;`
42+
// This function is now imported into `Stream::` for `Stream::send*()`.
43+
// Other classes inheriting from `Stream::` and implementing `read(uint8_t *buf, size_t size)`
44+
// must consequently use `int` as return type, namely Hardware/SoftwareSerial, FileSystems...
45+
#define STREAM_READ_RETURNS_INT 1
46+
47+
// Stream::send API is present
48+
#define STREAMSEND_API 1
49+
3850
class Stream: public Print {
3951
protected:
4052
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 {
5365
// parsing methods
5466

5567
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
68+
unsigned long getTimeout () const { return _timeout; }
5669

5770
bool find(const char *target); // reads data from the stream until the target string is found
5871
bool find(uint8_t *target) {
@@ -102,12 +115,114 @@ class Stream: public Print {
102115
virtual String readString();
103116
String readStringUntil(char terminator);
104117

118+
virtual int read (uint8_t* buffer, size_t len);
119+
int read (char* buffer, size_t len) { return read((uint8_t*)buffer, len); }
120+
121+
//////////////////// extension: direct access to input buffer
122+
// to provide when possible a pointer to available data for read
123+
124+
// informs user and ::to*() on effective buffered peek API implementation
125+
// by default: not available
126+
virtual bool hasPeekBufferAPI () const { return false; }
127+
128+
// returns number of byte accessible by peekBuffer()
129+
virtual size_t peekAvailable () { return 0; }
130+
131+
// returns a pointer to available data buffer (size = peekAvailable())
132+
// semantic forbids any kind of ::read()
133+
// - after calling peekBuffer()
134+
// - and before calling peekConsume()
135+
virtual const char* peekBuffer () { return nullptr; }
136+
137+
// consumes bytes after peekBuffer() use
138+
// (then ::read() is allowed)
139+
virtual void peekConsume (size_t consume) { (void)consume; }
140+
141+
// by default read timeout is possible (incoming data from network,serial..)
142+
// children can override to false (like String::)
143+
virtual bool inputCanTimeout () { return true; }
144+
145+
// (outputCanTimeout() is defined in Print::)
146+
147+
////////////////////////
148+
//////////////////// extensions: Streaming streams to streams
149+
// Stream::send*()
150+
//
151+
// Stream::send*() uses 1-copy transfers when peekBuffer API is
152+
// available, or makes a regular transfer through a temporary buffer.
153+
//
154+
// - for efficiency, Stream classes should implement peekAPI when
155+
// possible
156+
// - for an efficient timeout management, Print/Stream classes
157+
// should implement {output,input}CanTimeout()
158+
159+
using oneShotMs = esp8266::polledTimeout::oneShotFastMs;
160+
static constexpr int temporaryStackBufferSize = 64;
161+
162+
// ::send*() methods:
163+
// - always stop before timeout when "no-more-input-possible-data"
164+
// or "no-more-output-possible-data" condition is met
165+
// - always return number of transfered bytes
166+
// When result is 0 or less than requested maxLen, Print::getLastSend()
167+
// contains an error reason.
168+
169+
// transfers already buffered / immediately available data (no timeout)
170+
// returns number of transfered bytes
171+
size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
172+
size_t sendAvailable (Print& to) { return sendAvailable(&to); }
173+
174+
// transfers data until timeout
175+
// returns number of transfered bytes
176+
size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
177+
size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }
178+
179+
// transfers data until a char is encountered (the char is swallowed but not transfered) with timeout
180+
// returns number of transfered bytes
181+
size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
182+
size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }
183+
184+
// transfers data until requested size or timeout
185+
// returns number of transfered bytes
186+
size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
187+
size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }
188+
189+
// remaining size (-1 by default = unknown)
190+
virtual ssize_t streamRemaining () { return -1; }
191+
192+
enum class Report
193+
{
194+
Success = 0,
195+
TimedOut,
196+
ReadError,
197+
WriteError,
198+
ShortOperation,
199+
};
200+
201+
Report getLastSendReport () const { return _sendReport; }
202+
203+
protected:
204+
size_t sendGeneric (Print* to,
205+
const ssize_t len = -1,
206+
const int readUntilChar = -1,
207+
oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */);
208+
209+
size_t SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs);
210+
size_t SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs);
211+
size_t SendGenericRegular(Print* to, const ssize_t len, const oneShotMs::timeType timeoutMs);
212+
213+
void setReport (Report report) { _sendReport = report; }
214+
215+
private:
216+
217+
Report _sendReport = Report::Success;
218+
219+
//////////////////// end of extensions
220+
105221
protected:
106-
long parseInt(char skipChar); // as above but the given skipChar is ignored
107-
// as above but the given skipChar is ignored
222+
long parseInt(char skipChar); // as parseInt() but the given skipChar is ignored
108223
// this allows format characters (typically commas) in values to be ignored
109224

110-
float parseFloat(char skipChar); // as above but the given skipChar is ignored
225+
float parseFloat(char skipChar); // as parseFloat() but the given skipChar is ignored
111226
};
112227

113228
#endif

0 commit comments

Comments
 (0)