mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-11 15:22:13 +03:00
Stream::send() (#6979)
This commit is contained in:
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
|
Reference in New Issue
Block a user