mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-23 12:51:24 +01:00
RingStream RAM saver
This commit is contained in:
parent
7d6c2c8afb
commit
a5cda1e350
@ -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);
|
||||
}
|
||||
|
||||
|
15
RingStream.h
15
RingStream.h
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user