mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-02-17 06:29:15 +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/>.
|
* 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 "RingStream.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
const byte FLASH_INSERT_MARKER=0xff;
|
||||||
|
|
||||||
RingStream::RingStream( const uint16_t len)
|
RingStream::RingStream( const uint16_t len)
|
||||||
{
|
{
|
||||||
_len=len;
|
_len=len;
|
||||||
@ -31,6 +39,7 @@ RingStream::RingStream( const uint16_t len)
|
|||||||
_overflow=false;
|
_overflow=false;
|
||||||
_mark=0;
|
_mark=0;
|
||||||
_count=0;
|
_count=0;
|
||||||
|
_flashInsert=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RingStream::write(uint8_t b) {
|
size_t RingStream::write(uint8_t b) {
|
||||||
@ -46,8 +55,74 @@ size_t RingStream::write(uint8_t b) {
|
|||||||
return 1;
|
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() {
|
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
|
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];
|
byte b=_buffer[_pos_read];
|
||||||
_pos_read++;
|
_pos_read++;
|
||||||
if (_pos_read==_len) _pos_read=0;
|
if (_pos_read==_len) _pos_read=0;
|
||||||
@ -55,9 +130,8 @@ int RingStream::read() {
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int RingStream::count() {
|
int RingStream::count() {
|
||||||
return (read()<<8) | read();
|
return (readRawByte()<<8) | readRawByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
int RingStream::freeSpace() {
|
int RingStream::freeSpace() {
|
||||||
@ -83,6 +157,7 @@ uint8_t RingStream::peekTargetMark() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RingStream::commit() {
|
bool RingStream::commit() {
|
||||||
|
_flashInsert=NULL; // prepared for first read
|
||||||
if (_overflow) {
|
if (_overflow) {
|
||||||
DIAG(F("RingStream(%d) commit(%d) OVERFLOW"),_len, _count);
|
DIAG(F("RingStream(%d) commit(%d) OVERFLOW"),_len, _count);
|
||||||
// just throw it away
|
// just throw it away
|
||||||
@ -108,8 +183,7 @@ void RingStream::flush() {
|
|||||||
_pos_write=0;
|
_pos_write=0;
|
||||||
_pos_read=0;
|
_pos_read=0;
|
||||||
_buffer[0]=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 <Arduino.h>
|
||||||
|
#include "FSH.h"
|
||||||
|
|
||||||
class RingStream : public Print {
|
class RingStream : public Print {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RingStream( const uint16_t len);
|
RingStream( const uint16_t len);
|
||||||
|
static const int THIS_IS_A_RINGSTREAM=77;
|
||||||
virtual size_t write(uint8_t b);
|
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;
|
using Print::write;
|
||||||
|
size_t printFlash(const FSH * flashBuffer);
|
||||||
int read();
|
int read();
|
||||||
int count();
|
int count();
|
||||||
int freeSpace();
|
int freeSpace();
|
||||||
void mark(uint8_t b);
|
void mark(uint8_t b);
|
||||||
bool commit();
|
bool commit();
|
||||||
uint8_t peekTargetMark();
|
uint8_t peekTargetMark();
|
||||||
void printBuffer(Print * streamer);
|
|
||||||
void flush();
|
void flush();
|
||||||
|
byte readRawByte();
|
||||||
private:
|
private:
|
||||||
int _len;
|
int _len;
|
||||||
int _pos_write;
|
int _pos_write;
|
||||||
@ -45,6 +55,7 @@ class RingStream : public Print {
|
|||||||
int _mark;
|
int _mark;
|
||||||
int _count;
|
int _count;
|
||||||
byte * _buffer;
|
byte * _buffer;
|
||||||
|
char * _flashInsert;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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 's': stream->print(va_arg(args, char*)); break;
|
||||||
case 'e': printEscapes(stream,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 '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 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break;
|
||||||
case 'u': printPadded(stream,va_arg(args, unsigned 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;
|
case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break;
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#define StringFormatter_h
|
#define StringFormatter_h
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
|
#include "RingStream.h"
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_SAMD)
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
// Some processors use a gcc compiler that renames va_list!!!
|
// Some processors use a gcc compiler that renames va_list!!!
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
Loading…
Reference in New Issue
Block a user