Flash strings again


#1

node-name

FlashString/ProgmemString

Inputs

No inputs

Outputs

  • OUT (String) — A String constant in Flash memory

So, going back to my old post of: https://forum.xod.io/t/feature-request-constants-in-progmem/441/3

And working on my menu-ing system, it seems that unfortunately “constant” XStrings are copied to SRAM even though their wrapper class is declared static and global as so in the transpiled XOD code:

static XStringCString node_1_output_VAL = XStringCString("Hello, World!");

It seems to me, unless there’s something weird going on with my compiler, is the way avr-gcc works is that all string literals like the “Hello, World” XStringCString class initializer there (the class takes const char*), which while clearly are nominally stored in Flash program memory (where else would they be when the power is off?) are also loaded into SRAM on execution regardless, even though the wrapper XString-derived class which provides the view/iteration interface is static.

I wrote these two little modifications to the XOD utility classes CStringView and XStringCString:

class FlashStringView : public ListView<char> {
 public:
  class Cursor : public detail::Cursor<char> {
   public:
    Cursor(const char PROGMEM* str) : _ptr(str) {}

    bool isValid() const override {
      return static_cast<bool>(static_cast<char>(pgm_read_byte(_ptr)));
    }

    bool value(char* out) const override {
      *out = static_cast<char>(pgm_read_byte(_ptr));
      return static_cast<bool>(*out);
    }

    void next() override { ++_ptr; }

   private:
    const char PROGMEM* _ptr;
  };

 public:
  FlashStringView(const char PROGMEM* str = nullptr) : _str(str) {}
  FlashStringView(const __FlashStringHelper* str)
      : _str(reinterpret_cast<const char PROGMEM*>(str)) {}

  FlashStringView& operator=(const FlashStringView& rhs) {
    _str = rhs._str;
    return *this;
  }

  virtual Iterator<char> iterate() const override {
    return _str ? Iterator<char>(new Cursor(_str)) : Iterator<char>::nil();
  }

 private:
  friend class Cursor;
  const char PROGMEM* _str;
};


class XStringFlashString : public XString {
 public:
  XStringFlashString(const char PROGMEM* str) : XString(&_view), _view(str) {}

  XStringFlashString(const __FlashStringHelper* str)
      : XString(&_view), _view(str) {}

 private:
  FlashStringView _view;
};

Then just by changing the static initialization segment from what XOD transpiled before to:

const char node_1_output_text[] PROGMEM = {"Hello, World!"};
static XStringFlashString node_1_output_VAL = XStringFlashString(node_1_output_text);

This structure seems to be plug-n-play compatible with existing code and in my testing in a sketch with a lot of fixed strings to display provides a pretty significant SRAM savings! I mean I guess I’ve technically already written the bulk of the node I’m “requesting” here but it would be great if it were integrated into the IDE such that the actual text could be edited in a box without diving into code :slight_smile:


#2

As I mentioned in a previous topic, I think we need an editable code node