Flash Strings

Introduction

Strings are basically just arrays of char, but have additional methods to allow them to be used more easily. These methods are consistent with Wiring String, so should be reasonably familiar.

  • length() returns the number of characters in the String, excluding the NUL terminator
  • size() returns the number of bytes of storage used

For example, “123” is actually stored as { '1', '2', '3', '\0' } so the length is 3 and the size is 4. However, “1234” is stored as { '1', '2', '3', '4', '\0' } so the length is 4 and the size is 8.

Using Strings

Note

You can use FSTR::String or the Sming-provided FlashString alias to work with Strings.

Within a function:

DEFINE_FSTR_LOCAL(myFlashString, "This is my flash string");

Serial.println(myFlashString);
Serial.printf("myFlashString has %u chars and occupies %u bytes\n", myFlashString.length(), myFlashString.size());

To use Strings across translation units, we do this in the header:

DECLARE_FSTR(myFlashString);

And in a source file:

DEFINE_FSTR(myFlashString, "I am a flash string\0I've got a Naughty NUL.");

You can generally use a Flash String anywhere you can use a regular Wiring String as it has an implicit ::String() operator. Note that WString is used within the library for disambiguation.

Inline Strings

Use the FS() macro to create Flash Strings inline:

Serial.println(FS("A Flash String"));

Note

The macro makes use of FS_PTR() which creates the structure and returns a pointer to it. It behaves like a function call, although the compiler inlines the code.

Therefore FS() may only be used within functions. At file scope you’ll get this error:

statement-expressions are not allowed outside functions nor in template-argument lists

The example above doesn’t provide any improvement over F as there are no Flash String overloads available, so is equivalent to this:

String s = FS("A Flash String");
Serial.println(s);

However, it’s rather different if you pass it to a function which recognises Flash Strings, like this:

FSTR::println(Serial, FS("A Flash String"));

This is equivalent to:

FS("A Flash String").printTo(Serial);
Serial.println();

FSTR::String::printTo() uses no heap and imposes no restriction on the string length.

Nested Inline Strings

It would be really useful to be able to use inline Strings this within nested structures, and this can be done provided those structures are in RAM.

Important

Inline Strings cannot be used when defining Vectors or Maps.

Here’s is a simplified structure we will attempt to initialize:

static const struct {
   FlashString* string;
} flashData PROGMEM = { FS_PTR("Inline Flash String") };
Serial.println(*flashData.string);

The static flashData structure gets initialised at runtime on first use, as per C++ rules. This attempts to copy our pointer into the flashData structure which clearly it cannot do as it’s in PROGMEM, so we get a LOAD/STORE error. We must remove PROGMEM.

Avoiding the heap

Instead of using a temporary Wiring String, you can use LOAD_FSTR() to load the content into a temporary stack buffer:

DEFINE_FSTR(globalTest, "This is a testing string");

void func()
{
   LOAD_FSTR(local, globalTest);
   printf("%s, %u characters, buffer is %u bytes\n", local, globalTest.length(), sizeof(local));
}

You can do this with inline Flash Strings using FSTR_ARRAY():

FSTR_ARRAY(buffer, "text");

Is roughly equivalent to:

char name[] = "text";

Except the buffer is word aligned, so sizeof(name) may differ.

Macros

FS_PTR(str)

Define an inline String and return a pointer to it.

Note
The rather obscure asm statement is required to prevent the compiler from discarding the symbol at link time, which leads to an ‘undefined reference’ error

FS(str)

Define an inline FSTR::String and return it as a copy.

Example:

    Serial.println(FS("This is a Flash String"));

DECLARE_FSTR(name)

Declare a global FSTR::String& reference.

Note
Define the FSTR::String object using DEFINE_STR()
Parameters
  • name:

DEFINE_FSTR(name, str)

Define a FSTR::String object with global reference.

Example:

Parameters

DEFINE_FSTR(test, “This is a test\0Another test\0hello”)

The data includes the nul terminator but the length does not.

DEFINE_FSTR_LOCAL(name, str)

Like DEFINE_FSTR except reference is declared static constexpr.

DEFINE_FSTR_DATA(name, str)

Define a FSTR::String data structure.

Parameters
  • name: Name of data structure
  • str: Quoted string content

LOAD_FSTR(name, fstr)

Load a FSTR::String object into a named local (stack) buffer.

Example:

DEFINE_FSTR(globalTest, "This is a testing string")
...
LOAD_FSTR(local, globalTest)
printf("%s, %u characters, buffer is %u bytes\n", local, globalTest.length(), sizeof(local));

FSTR_ARRAY(name, str)

Define a flash FSTR::String and load it into a named char[] buffer on the stack.

Note
Equivalent to char name[] = "text" except the buffer is word aligned. Faster than using a temporary Wiring String and avoids using the heap.
Parameters

IMPORT_FSTR(name, file)

Define a FSTR::String containing data from an external file.

See
See also IMPORT_FSTR_DATA
Parameters
  • name: Name for the FSTR::String object
  • file: Absolute path to the file containing the content

IMPORT_FSTR_LOCAL(name, file)

Like IMPORT_FSTR except reference is declared static constexpr.

FSTR_TABLE(name)

declare a table of FlashStrings

Parameters
  • name: name of the table

Declares a simple table. Example:

DEFINE_FSTR(fstr1, "Test string #1");
DEFINE_FSTR(fstr2, "Test string #2");

FSTR_TABLE(table) = {
    &fstr1,
    &fstr2,
};

Table entries may be accessed directly as they are word-aligned. Examples:

debugf("fstr1 = '%s'", FSTR::String(*table[0]).c_str());
debugf("fstr2.length() = %u", table[1]->length());

String Class

class String : public FSTR::Object<String, char>

describes a counted string stored in flash memory

Public Types

template<>
using Iterator = ObjectIterator<String, char>

Public Functions

size_t size() const

Get the number of bytes used to store the String.

Note
Always an integer multiple of 4 bytes

flash_string_t data() const

Get a WString-compatible pointer to the flash data.

bool equals(const char *cstr, size_t len = 0) const

Check for equality with a C-string.

Note
loads string into a stack buffer for the comparison, no heap required
Parameters
  • cstr:
  • len: Length of cstr (optional)
Return Value
  • bool: true if strings are identical

bool equals(const String &str) const

Check for equality with another String.

Parameters
  • str:
Return Value
  • bool: true if strings are identical

bool operator==(const char *str) const
bool operator==(const String &str) const
bool operator!=(const char *str) const
bool operator!=(const String &str) const
operator WString() const
bool equals(const WString &str) const
bool equalsIgnoreCase(const WString &str) const
bool operator==(const WString &str) const
bool operator!=(const WString &str) const
StringPrinter printer() const

Supports printing of large String objects.

Avoids implicit String() cast when working with large FlashStrings:

IMPORT_FSTR(largeString, PROJECT_DIR "/files/large-text.txt");
    Serial.println(largeString.printer());

size_t printTo(Print &p) const
Iterator begin() const
Iterator end() const
size_t length() const

Get the length of the content in elements.

int indexOf(const ValueType &value) const
char valueAt(unsigned index) const
char operator[](unsigned index) const

Array operator[].

size_t elementSize() const
size_t read(size_t index, char *buffer, size_t count) const

Read content into RAM.

Parameters
  • index: First element to read
  • buffer: Where to store data
  • count: How many elements to read
Return Value
  • size_t: Number of elements actually read

size_t read(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM.

Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

size_t readFlash(size_t index, char *buffer, size_t count) const

Read content into RAM,using flashmem_read()

Parameters
  • index: First element to read
  • buffer: Where to store data
  • count: How many elements to read
Return Value
  • size_t: Number of elements actually read

size_t readFlash(size_t offset, void *buffer, size_t count) const

Read contents of a String into RAM, using flashread()

PROGMEM data is accessed via the CPU data cache, so to avoid degrading performance you can use this method to read data directly from flash memory. This is appropriate for infrequently accessed data, especially if it is large. For example, if storing content using

IMPORT_FSTR instead of SPIFFS then it is generally better to avoid contaminating the cache.
See
See also FlashMemoryStream class.
Parameters
  • offset: Zero-based offset from start of flash data to start reading
  • buffer: Where to store data
  • count: How many bytes to read
Return Value
  • size_t: Number of bytes actually read

template <class ObjectType>
constexpr const ObjectType &as() const

Cast to a different object type.

Note
example:
    fstr.as<Array<int>>();

bool isCopy() const
bool isNull() const

Indicates an invalid String, used for return value from lookups, etc.

Note
A real String can be zero-length, but it cannot be null

Public Members

uint32_t flashLength_

Public Static Functions

static const String &empty()

Return an empty object which evaluates to null.