Maps

Introduction

A FSTR::Map is analogous to the Wiring HashMap class, allowing content to be indexed using a key value.

The Map contains an array of FSTR::MapPair structures:

struct MapPair<KeyType, ContentType> {
   KeyType key_;
   ContentType* content_;
};

KeyType can be any simple type such as char, int, float, enum etc. It may also be a String Object (or, more precisely, String*).

ContentType can be any Object type (String, Array, Vector or Map). This allows hierarchical structures to be created.

Example: int ⇒ String

Here’s a basic example using integer keys:

#include <FlashString/Map.hpp>

IMPORT_FSTR(content1, PROJECT_DIR "/files/index.html");
IMPORT_FSTR(content2, PROJECT_DIR "/files/favicon.html");

DEFINE_FSTR_MAP(intmap, int, FSTR::String,
   {35, &content1},
   {180, &content2}
);

We can now do this:

void printValue(int key)
{
   auto value = intmap[key];
   if(value) {
      Serial.printf("Found '%u' in map, containing %u chars\n", value.key(), value.content().length());
      Serial.println(value.printer());
   } else {
      Serial.printf("Couldn't find '%u' in map\n", key);
   }
}

Example: String ⇒ String

Both the key and the content are stored as Strings:

#include <FlashString/Map.hpp>

DEFINE_FSTR_LOCAL(key1, "index.html");
DEFINE_FSTR_LOCAL(key2, "favicon.ico");
IMPORT_FSTR(content1, PROJECT_DIR "/files/index.html");
IMPORT_FSTR(content2, PROJECT_DIR "/files/favicon.html");

DEFINE_FSTR_MAP(fileMap, FlashString, FlashString,
   {&key1, &content1},
   {&key2, &content2},
);

We can now do this:

void onFile(HttpRequest& request, HttpResponse& response)
{
   String fileName = request.uri.getRelativePath();
   auto& value = fileMap[fileName];
   if(value) {
      // Found
      Serial.printf("Found '%s' in fileMap\n", String(value.key()).c_str());
      auto stream = new FlashMemoryStream(value);
      response.sendDataStream(stream, ContentType::fromFullFileName(fileName));
   } else {
      Serial.printf("File '%s' not found\n", fileName.c_str());
   }
}

Note

As with Vector<String>, Map<String, ...> lookups are by default case-insensitive.

If you require a case-sensitive lookup, use the indexOf method with ignoreCase = false.

Structure

The macro in the first example above produces a structure like this:

constexpr const struct {
   ObjectBase object;
   MapPair<int, String> data[2];
} __fstr__intmap PROGMEM = {
   {16},
   {35, &content1},
   {180, &content2},
};
const Map<int, String>& intmap = __fstr__intmap.object.as<Map<int, String>>();

Note: FSTR:: namespace qualifier omitted for clarity.

Usually, each MapPair is 8 bytes, but if the key is a double or int64 it would be 12 bytes.

Macros

DEFINE_FSTR_MAP(name, KeyType, ContentType, ...)
DEFINE_FSTR_MAP_LOCAL(name, KeyType, ContentType, ...)

Like DEFINE_FSTR_MAP except reference is declared static constexpr.

DEFINE_FSTR_MAP_SIZED(name, KeyType, ContentType, size, ...)
DEFINE_FSTR_MAP_SIZED_LOCAL(name, KeyType, ContentType, size, ...)

Like DEFINE_FSTR_MAP_SIZED except reference is declared static.

DEFINE_FSTR_MAP_DATA(name, KeyType, ContentType, ...)

Define a Map data structure.

Note
Size will be calculated
Parameters
  • name: Name of data structure
  • KeyType: Integral type to use for key
  • ContentType: Object type to declare for content
  • ...: List of MapPair definitions { key, &content }

DEFINE_FSTR_MAP_DATA_SIZED(name, KeyType, ContentType, size, ...)

Define a Map data structure, specifying the number of elements.

Parameters
  • name: Name of data structure
  • KeyType: Integral type to use for key
  • ContentType: Object type to declare for content
  • size: Number of elements
  • ...: List of MapPair definitions { key, &content }

const Pair FSTR::Map::valueAt(unsigned index) const

Get a map entry by index, if it exists.

Note
Result validity can be checked using if()

template <typename TRefKey, typename T = KeyType>
std::enable_if<!std::is_class<T>::value, int>::type FSTR::Map::indexOf(const TRefKey &key) const

Lookup an integral key and return the index.

Parameters
  • key: Key to locate, must be compatible with KeyType for equality comparison
Return Value
  • int: If key isn’t found, return -1

template <typename TRefKey, typename T = KeyType>
std::enable_if<std::is_same<T, String>::value, int>::type FSTR::Map::indexOf(const TRefKey &key, bool ignoreCase = true) const

Lookup a String key and return the index.

Parameters
  • key:
  • ignoreCase: Whether search is case-sensitive (default: true)
Return Value
  • int: If key isn’t found, return -1

template <typename TRefKey>
const Pair FSTR::Map::operator[](const TRefKey &key) const

Lookup a key and return the entry, if found.

Note
Result validity can be checked using if()
Parameters
  • key:

MapPrinter<Map> FSTR::Map::printer() const

Returns a printer object for this array.

Note
ElementType must be supported by Print

size_t FSTR::Map::printTo(Print &p) const
DECLARE_FSTR_MAP(name, KeyType, ContentType)

Declare a global Map& reference.

Note
Use DEFINE_FSTR_MAP to instantiate the global object
Parameters
  • name:
  • KeyType: Integral type to use for key
  • ContentType: Object type to declare for content

template <typename KeyType, class ContentType, class Pair = MapPair<KeyType, ContentType>>
class Map : public FSTR::Object<Map<KeyType, ContentType>, Pair>
#include <Map.hpp>

Class template to access an associative map.

Template Parameters

Public Types

template<>
using Iterator = ObjectIterator<Map<KeyType, ContentType>, Pair>

Public Functions

const Pair valueAt(unsigned index) const

Get a map entry by index, if it exists.

Note
Result validity can be checked using if()

template <typename TRefKey, typename T = KeyType>
std::enable_if<!std::is_class<T>::value, int>::type indexOf(const TRefKey &key) const

Lookup an integral key and return the index.

Parameters
  • key: Key to locate, must be compatible with KeyType for equality comparison
Return Value
  • int: If key isn’t found, return -1

template <typename TRefKey, typename T = KeyType>
std::enable_if<std::is_same<T, String>::value, int>::type indexOf(const TRefKey &key, bool ignoreCase = true) const

Lookup a String key and return the index.

Parameters
  • key:
  • ignoreCase: Whether search is case-sensitive (default: true)
Return Value
  • int: If key isn’t found, return -1

template <typename TRefKey>
const Pair operator[](const TRefKey &key) const

Lookup a key and return the entry, if found.

Note
Result validity can be checked using if()
Parameters
  • key:

MapPrinter<Map> printer() const

Returns a printer object for this array.

Note
ElementType must be supported by Print

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
Pair operator[](unsigned index) const

Array operator[].

size_t elementSize() const
const Pair *data() const
size_t read(size_t index, Pair *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, Pair *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

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

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 Map<KeyType, ContentType> &empty()

Return an empty object which evaluates to null.

template <typename KeyType, class ContentType>
class MapPair
#include <MapPair.hpp>

describes a pair mapping key => data for a specified key type

Template Parameters

Public Types

template<>
typedef void (MapPair::*IfHelperType)() const

Public Functions

void IfHelper() const
operator IfHelperType() const

Provides bool() operator to determine if Pair is valid.

template <typename T = KeyType>
std::enable_if<!std::is_class<T>::value, KeyType>::type key() const

Get the key (non-class key types)

template <typename T = KeyType>
std::enable_if<std::is_same<T, String>::value, const KeyType&>::type key() const

Get the key (String key type)

const ContentType &content() const

Accessor to get a reference to the content.

operator const ContentType&() const
operator WString() const
size_t printTo(Print &p) const

Public Members

KeyStoreType key_
const ContentType *content_

Public Static Functions

static const MapPair empty()

Get an empty Pair object, identifies as invalid when lookup fails.

Class Templates

template <typename KeyType, class ContentType, class Pair = MapPair<KeyType, ContentType>>
class Map : public FSTR::Object<Map<KeyType, ContentType>, Pair>

Class template to access an associative map.

Template Parameters

Public Types

template<>
using Iterator = ObjectIterator<Map<KeyType, ContentType>, Pair>

Public Functions

const Pair valueAt(unsigned index) const

Get a map entry by index, if it exists.

Note
Result validity can be checked using if()

template <typename TRefKey, typename T = KeyType>
std::enable_if<!std::is_class<T>::value, int>::type indexOf(const TRefKey &key) const

Lookup an integral key and return the index.

Parameters
  • key: Key to locate, must be compatible with KeyType for equality comparison
Return Value
  • int: If key isn’t found, return -1

template <typename TRefKey, typename T = KeyType>
std::enable_if<std::is_same<T, String>::value, int>::type indexOf(const TRefKey &key, bool ignoreCase = true) const

Lookup a String key and return the index.

Parameters
  • key:
  • ignoreCase: Whether search is case-sensitive (default: true)
Return Value
  • int: If key isn’t found, return -1

template <typename TRefKey>
const Pair operator[](const TRefKey &key) const

Lookup a key and return the entry, if found.

Note
Result validity can be checked using if()
Parameters
  • key:

MapPrinter<Map> printer() const

Returns a printer object for this array.

Note
ElementType must be supported by Print

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
Pair operator[](unsigned index) const

Array operator[].

size_t elementSize() const
const Pair *data() const
size_t read(size_t index, Pair *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, Pair *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

size_t size() const

Get the object data size in bytes.

Note
Always an integer multiple of 4 bytes

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 Map<KeyType, ContentType> &empty()

Return an empty object which evaluates to null.

template <typename KeyType, class ContentType>
class MapPair

describes a pair mapping key => data for a specified key type

Template Parameters

Public Types

template<>
typedef void (MapPair::*IfHelperType)() const

Public Functions

void IfHelper() const
operator IfHelperType() const

Provides bool() operator to determine if Pair is valid.

template <typename T = KeyType>
std::enable_if<!std::is_class<T>::value, KeyType>::type key() const

Get the key (non-class key types)

template <typename T = KeyType>
std::enable_if<std::is_same<T, String>::value, const KeyType&>::type key() const

Get the key (String key type)

const ContentType &content() const

Accessor to get a reference to the content.

operator const ContentType&() const
operator WString() const
size_t printTo(Print &p) const

Public Members

KeyStoreType key_
const ContentType *content_

Public Static Functions

static const MapPair empty()

Get an empty Pair object, identifies as invalid when lookup fails.