1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-22 23:56:13 +01:00

RingStream RAM saver

This commit is contained in:
Asbelos 2022-06-27 12:42:59 +01:00
parent 7d6c2c8afb
commit a5cda1e350
4 changed files with 110 additions and 9 deletions

View File

@ -18,9 +18,17 @@
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
// 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<uintptr_t>(flash); // expect size match with pointer
// write address bytes LSB first (size depends on CPU)
for (byte f=0;f<sizeof(iFlash); f++) {
write((byte) (iFlash & 0xFF));
iFlash>>=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<sizeof(iFlash); f++) {
uintptr_t bf=readRawByte();
bf&=0x00ff;
bf<<= (8*f); // shift byte to correct position in iFlash
iFlash |= bf;
}
_flashInsert=reinterpret_cast<char * >( 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);
}

View File

@ -21,22 +21,32 @@
*/
#include <Arduino.h>
#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

View File

@ -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;

View File

@ -20,6 +20,8 @@
#define StringFormatter_h
#include <Arduino.h>
#include "FSH.h"
#include "RingStream.h"
#if defined(ARDUINO_ARCH_SAMD)
// Some processors use a gcc compiler that renames va_list!!!
#include <cstdarg>