-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Stream::send() #6979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stream::send() #6979
Changes from 250 commits
c28e50f
b2dfcfa
5235e73
f5cdd10
c3148c4
40b782c
08fe86e
643dd6d
07b52b5
429ba2f
64c2656
6b14a13
32f8871
90daea7
cf892fd
c0f4a16
dfa48c6
e521195
8d3e51f
3345ef1
7cd36a9
a5658fe
92c99f2
474d5a1
314da51
5d4a507
d20e867
630b6b6
3edf028
ac86464
03519da
bc2b59a
72c3237
7db503a
048aecf
58a6257
fd32e65
e8ffe6d
e480ea5
15ab6cf
9dcc444
3b7effe
e84d9f5
beee304
0d6a4af
2036749
5e7c448
afc2494
dd45763
4196458
f7af80b
d69028c
34a6d00
b3c1e27
4071398
0c3a372
820acf0
8e44e02
d44a839
0e29771
df38f89
1c1bd76
2b637ae
4ccc136
5577b32
db62df4
ec3ffe0
fb2c546
99740f8
fee1307
cc3e01c
caa8f2c
cee7631
968e74f
18a842c
0bfc6e9
9387a25
a6f9518
dca0561
fef29c9
fe9c0a1
66ab5bd
957bb36
ef587f5
09fd253
7027de6
0ee1c96
88264aa
f788e52
d005bbf
225b7c6
fac8992
0a540a6
6898f5e
679134c
dc30f67
13e8512
b5ced49
51b6961
3f9d4cc
b80bfff
d62c719
192faab
951737c
ca27dd7
59da9a4
1ce2351
84a3137
dfb39ca
ceaaf18
0e443d0
42ffd6f
feaccaa
580d553
4d2ce85
bf9ad75
cdd72f9
b69bf75
3350923
f0483e1
9b21424
443f5b4
f290fd5
4ba0805
55bb160
ce3c922
bc29fe6
dd947c8
81f2d6f
f85ddbb
1fd80d1
e96f80d
36a0ddc
10e86ad
915647a
dc7021e
a2cb9b1
9021ed8
b52e61b
46808cc
5f80dce
143f2d6
f692c09
ff1ea5b
0437782
3745126
ddb7242
dcb1085
45f7f6c
e47c162
d4f32af
ef998c0
39f6f56
815b82c
d85f50f
112313d
a378aff
f9a5f18
6d84820
9bc8146
a99c418
3699ad3
8e58e97
b24b7ef
ac223a9
0eb0b55
d6c7203
9d90286
270ce0a
f98e167
211c914
c56e533
8268807
d5d2ee5
9eca03c
0901039
68aa236
2e9d1a0
8e350e0
c7fae60
e24e26c
f314946
cde30e4
d95ddf2
44e585f
6b7c344
b31ec10
e326b0a
0f5d283
06f93a0
ca821a9
3a308c2
afd5954
e786a13
5e4342d
ad580e0
8e064be
93c8cc7
30be04c
c94ba0b
31e3c39
da1f96d
531d794
cd3caad
746fcd2
7b35d70
21a50ed
92a6302
41d1868
abca00b
3c1e104
4779433
84d8bc6
592b440
2783c19
da1240a
5ab7415
b70a002
395dae3
9035f51
94e8d60
36a6bce
711bfe8
dae26cb
e1e6fdb
784771c
908f980
b73a9ca
e0084f3
44fb378
3be63d5
e299474
085dbd2
354288b
c223265
756ca72
747a1a9
bfe7ee7
c0a1c38
1188958
bfad4fc
70eb9e8
d32361c
e88d21e
594d77b
b4ca0dd
81290b6
32eb9bf
f0498b5
fd5f282
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#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 | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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,106 @@ 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); } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this not virtual? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
//////////////////// 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 peekBufferAPI () const { return false; } | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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 inputTimeoutPossible () { return true; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are these public? I can't think of a use case where they would be used from a sketch. class Stream : public Print
{
...
size_t toFull(); //non buffered version, i.e.: the case that currently would return hasPeekBuffer = false => the else body of that mega if-else clause in toFull()
};
class StreamPeekBuffered : public Stream
{
...
size_t toFull(); //buffered version, i.e.: the if body of the mega if-else in toFull()
};
Then, unbuffered classes would inherit directly from Stream as they do now, and buffered classes would inherit from StreamPeekBuffered and have these functions available internally. In other words, the user shouldn't need use these or even know about them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider renaming to hasInputTimeout() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think I want that 😆 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not a good idea to expose everything in a class. The private/protected keywords exist for very good reasons. You should hide implementation details whenever possible, especially in cases where methods are really internal details.
The line is being kept straight, hierarchy is straight top to bottom. I am just suggesting to implement common functionality in a base class put in between. That is trivial to implement, and it expresses your real intent:
The point here is that Streams that aren't buffered shouldn't be polluted with dummy implementations of these methods that don't apply to them. 3rd party libs that have objects that inherit from Stream won't be using these methods in their standard implementations, so they should inherit directly from Stream anyways. Unless they actually want to have implementations specific to our ESP and our peekbuffer, in which case they should inherit from out StreamPeekBuffer. From an user interface point of view, handling via a Stream base pointer is still ok for both case, as it should be. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get it, because The peekBuffer API is in the public interface to make it accessible by users. By these functions output buffers are directly accessible without copies. Why would we want to hide it ? |
||
|
||
// (outputTimeoutPossible() is defined in Print::) | ||
|
||
//////////////////// extensions: Streaming streams to streams | ||
// Stream::to*() | ||
// | ||
// Stream::to*() 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}TimeoutPossible() | ||
|
||
using oneShotMs = esp8266::polledTimeout::oneShotFastMs; | ||
|
||
// ::to*() 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 sendNow (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
size_t sendNow (Print& to) { return sendNow(&to); } | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// transfers data until timeout | ||
devyte marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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 streamSize () { return -1; } | ||
|
||
typedef enum | ||
{ | ||
STREAMSEND_SUCCESS = 0, | ||
STREAMSEND_TIMED_OUT, | ||
STREAMSEND_READ_ERROR, | ||
STREAMSEND_WRITE_ERROR, | ||
STREAMSEND_SHORT, | ||
} sendReport_e; | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
sendReport_e 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 maxLen = -1, | ||
const int readUntilChar = -1, | ||
oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */); | ||
|
||
//////////////////// 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 | ||
|
||
sendReport_e _sendReport = STREAMSEND_SUCCESS; | ||
}; | ||
|
||
#endif |
Uh oh!
There was an error while loading. Please reload this page.