diff --git a/RingStream.cpp b/RingStream.cpp
index bf89e33..a03bfbf 100644
--- a/RingStream.cpp
+++ b/RingStream.cpp
@@ -18,9 +18,17 @@
* along with CommandStation. If not, see .
*/
+
+// NOTE: The use of a marker byte without an escape algorithm means
+// RingStream is unsuitable for binary data. Should binary data need to be
+// streamed it will be necessary to implementr an escape strategy to handle the
+// marker char when embedded in data.
+
#include "RingStream.h"
#include "DIAG.h"
+const byte FLASH_INSERT_MARKER=0xff;
+
RingStream::RingStream( const uint16_t len)
{
_len=len;
@@ -31,6 +39,7 @@ RingStream::RingStream( const uint16_t len)
_overflow=false;
_mark=0;
_count=0;
+ _flashInsert=0;
}
size_t RingStream::write(uint8_t b) {
@@ -46,8 +55,74 @@ size_t RingStream::write(uint8_t b) {
return 1;
}
+// Ideally, I would prefer to override the Print:print(_FlashStringHelper) function
+// but the library authors omitted to make this virtual.
+// Therefore we obveride the only other simple function that has no side effects
+// in order that StringFormatter can recognise a RingStream and call its
+// printFlash() directly.
+int RingStream::availableForWrite() {
+ return THIS_IS_A_RINGSTREAM;
+ }
+
+size_t RingStream::printFlash(const FSH * flashBuffer) {
+// We are about to add a PROGMEM string to the buffer.
+// To save RAM we can insert a marker and the
+// progmem address into the buffer instead.
+// The buffer reading code must recognise this marker and
+// silently extract the progmem bytes.
+// In addition, we must make the count correct as if the
+// string had been embedded so that things like the wifi code
+// can read the expected count before reading the buffer.
+
+// Establish the actual length of the progmem string.
+char * flash=(char *)flashBuffer;
+int16_t plength=strlen_P(flash);
+if (plength==0) return 0; // just ignore empty string
+
+// Retain the buffer count as it will be modified by the marker+address insert
+int prevCount=_count;
+write(FLASH_INSERT_MARKER); // write the marker
+uintptr_t iFlash=reinterpret_cast(flash); // expect size match with pointer
+
+// write address bytes LSB first (size depends on CPU)
+for (byte f=0;f>=8;
+}
+
+// correct the buffer count to reflect the flash length, not the marker/addr.
+_count=prevCount+plength;
+return plength;
+}
+
int RingStream::read() {
+ if (_flashInsert) {
+ // we are reading out of a flash string
+ byte fb=GETFLASH(_flashInsert);
+ _flashInsert++;
+ if (fb) return fb; // we have a byte from the flash
+ // flash insert complete, clear and drop through to next buffer byte
+ _flashInsert=NULL;
+ }
if ((_pos_read==_pos_write) && !_overflow) return -1; // empty
+ byte b=readRawByte();
+ if (b!=FLASH_INSERT_MARKER) return b;
+
+ // Detected a flash insert
+ // read address bytes LSB first (size depends on CPU)
+ uintptr_t iFlash=0;
+ for (byte f=0; f( iFlash);
+ // and try again... so will read the first byte of the insert.
+ return read();
+}
+
+byte RingStream::readRawByte() {
byte b=_buffer[_pos_read];
_pos_read++;
if (_pos_read==_len) _pos_read=0;
@@ -55,9 +130,8 @@ int RingStream::read() {
return b;
}
-
int RingStream::count() {
- return (read()<<8) | read();
+ return (readRawByte()<<8) | readRawByte();
}
int RingStream::freeSpace() {
@@ -83,6 +157,7 @@ uint8_t RingStream::peekTargetMark() {
}
bool RingStream::commit() {
+ _flashInsert=NULL; // prepared for first read
if (_overflow) {
DIAG(F("RingStream(%d) commit(%d) OVERFLOW"),_len, _count);
// just throw it away
@@ -108,8 +183,7 @@ void RingStream::flush() {
_pos_write=0;
_pos_read=0;
_buffer[0]=0;
+ _flashInsert=NULL; // prepared for first read
+
}
-void RingStream::printBuffer(Print * stream) {
- _buffer[_pos_write]='\0';
- stream->print((char *)_buffer);
-}
+
diff --git a/RingStream.h b/RingStream.h
index 5821bc4..61c1737 100644
--- a/RingStream.h
+++ b/RingStream.h
@@ -21,22 +21,32 @@
*/
#include
+#include "FSH.h"
class RingStream : public Print {
public:
RingStream( const uint16_t len);
-
+ static const int THIS_IS_A_RINGSTREAM=77;
virtual size_t write(uint8_t b);
+
+ // This availableForWrite function is subverted from its original intention so that a caller
+ // can destinguish between a normal stream and a RingStream.
+ // The Arduino compiler does not support runtime dynamic cast to perform
+ // an instranceOf check.
+ // This is necessary since the Print functions are mostly not virtual so
+ // we cant override the print(__FlashStringHelper *) function.
+ virtual int availableForWrite() override;
using Print::write;
+ size_t printFlash(const FSH * flashBuffer);
int read();
int count();
int freeSpace();
void mark(uint8_t b);
bool commit();
uint8_t peekTargetMark();
- void printBuffer(Print * streamer);
void flush();
+ byte readRawByte();
private:
int _len;
int _pos_write;
@@ -45,6 +55,7 @@ class RingStream : public Print {
int _mark;
int _count;
byte * _buffer;
+ char * _flashInsert;
};
#endif
diff --git a/StringFormatter.cpp b/StringFormatter.cpp
index 485d8fd..ff5a96a 100644
--- a/StringFormatter.cpp
+++ b/StringFormatter.cpp
@@ -97,7 +97,21 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
case 's': stream->print(va_arg(args, char*)); break;
case 'e': printEscapes(stream,va_arg(args, char*)); break;
case 'E': printEscapes(stream,(const FSH*)va_arg(args, char*)); break;
- case 'S': stream->print((const FSH*)va_arg(args, char*)); break;
+ case 'S':
+ {
+ const FSH* flash= (const FSH*)va_arg(args, char*);
+
+#if WIFI_ON | ETHERNET_ON
+ // RingStream has special logic to handle flash strings
+ // but is not implemented unless wifi or ethernet are enabled.
+ // The define prevents RingStream code being added unnecessariliy.
+ if (stream->availableForWrite()==RingStream::THIS_IS_A_RINGSTREAM)
+ ((RingStream *)stream)->printFlash(flash);
+ else
+#endif
+ stream->print(flash);
+ break;
+ }
case 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break;
case 'u': printPadded(stream,va_arg(args, unsigned int), formatWidth, formatLeft); break;
case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break;
diff --git a/StringFormatter.h b/StringFormatter.h
index 50ce0f7..1e225b5 100644
--- a/StringFormatter.h
+++ b/StringFormatter.h
@@ -20,6 +20,8 @@
#define StringFormatter_h
#include
#include "FSH.h"
+#include "RingStream.h"
+
#if defined(ARDUINO_ARCH_SAMD)
// Some processors use a gcc compiler that renames va_list!!!
#include