Sming Framework API
Sming - Open Source framework for high efficiency WiFi SoC ESP8266 native development with C++ language.
FlashString.h
1 /****
2  * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
3  * Created 2015 by Skurydin Alexey
4  * http://github.com/SmingHub/Sming
5  * All files of the Sming Core are provided under the LGPL v3 license.
6  *
7  * @author: 2018 - Mikee47 <mike@sillyhouse.net>
8  *
9  * Defines the FlashString structure and associated macros for efficient flash memory string access.
10  *
11  ****/
12 
13 /*
14  * Note on storing strings in flash memory
15  *
16  * A string literal (e.g. "string") used in code gets emitted to the .rodata segment by the compiler.
17  * That means it gets read into RAM at startup and remains there.
18  * To avoid this, and reclaim the RAM, the data must be stored in a different segment. This is done
19  * using the PROGMEM macro, defined in FakePgmSpace.h.
20  *
21  * Once in flash memory, string data must be read into RAM before it can be used. Accessing the flash
22  * memory directly is awkard. If locations are not strictly accessed as 4-byte words the system will probably
23  * crash; I say 'probably' because sometimes it just behaves weirdly if the RAM address isn't aligned.
24  *
25  * _FakePgmSpace_ provides the basic mechanisms for storing and reading flash strings, including
26  * general-purpose string library functions. These are well-documented Arduino-compatible routines.
27  * Some additions have been made to Sming to cater for the ESP8266 use of these strings.
28  *
29  * F(string_literal) - loads a String object with the given text, which is allocated to flash.
30  * String s = F("test");
31  *
32  * _F(string_literal) - Like F() except buffer is allocated on stack. Most useful where nul-terminated data is required.
33  * m_printf(_F("C-style string\n"));
34  *
35  * DEFINE_PSTR(_name, _str) - declares a PSTR stored in flash. The variable (_name) points to flash
36  * memory so must be accessed using the appropriate xxx_P function.
37  *
38  * LOAD_PSTR(_name, _flash_str) - loads pre-defined PSTR into buffer on stack
39  * static DEFINE_PSTR(testFlash, "This is a test string\n"); // Function scope requires static allocation
40  * LOAD_PSTR(test, testFlash)
41  * m_printf(test);
42  *
43  * PSTR_ARRAY(_name, _str) - creates and loads string into named stack buffer
44  * Ensures loaded string stays in scope, unlike _F()
45  * String testfunc() {
46  * PSTR_ARRAY(test, "This is the test string");
47  * m_printf(test);
48  * ...
49  * return test;
50  * }
51  *
52  * IMPORT_FSTR(name, file) - binds a file into the firmware image as a FlashString object
53  * This needs to be used at file scope, example:
54  * IMPORT_FSTR(myFlashData, "files/myFlashData.bin")
55  *
56  * Both DEFINE_PSTR and PSTR_ARRAY load a PSTR into a stack buffer, but using sizeof() on that buffer will return
57  * a larger value than the string itself because it's aligned. Calling sizeof() on the original flash data will
58  * get the right value. If it's a regular nul-terminated string then strlen_P() will get the length, although it's
59  * time-consuming.
60  *
61  * FlashString
62  *
63  * A 'counted string' defined as a C++ structure with a length field.
64  * Requires only a single flash read to get the length; no strlen_P, etc.
65  * Stores arbitrary data including nuls.
66  * A global instance can be shared amongst modules with no loss of efficiency.
67  * The data is word-aligned so reading is as fast as possible; there are no alignment issues to deal with.
68  * Structure-based, so can add methods to the structure instead of using macros.
69  * Strongly typed, so support can be added to other modules for implicit conversions, etc. (e.g. ArduinoJson)
70  * Works with updated String class to provide the simplest string/data management.
71  *
72  * Macros are consistent with the PSTR types, but named 'FSTR'.
73  *
74  * Notes on usage
75  *
76  * Best practice is usually to define constant data at the top of a module. Non-trivial strings are no different.
77  * The mechanisms offered here provide a way to help do that.
78  *
79  */
80 
81 #ifndef __FLASH_STRING_H_
82 #define __FLASH_STRING_H_
83 
84 #include "WString.h"
85 
93 #define DEFINE_FSTR(_name, _str) \
94  DEFINE_FSTR_STRUCT(_##_name, _str) \
95  const FlashString& _name = _##_name.fstr;
96 
97 #define DEFINE_FSTR_STRUCT(_name, _str) \
98  constexpr struct { \
99  FlashString fstr; \
100  char data[ALIGNUP(sizeof(_str))]; \
101  } _name PROGMEM = {{sizeof(_str) - 1}, _str};
102 
103 // Declare a global reference to a FlashString instance
104 #define DECLARE_FSTR(_name) extern const FlashString& _name;
105 
106 // Get a pointer to the actual FlashString, used when creating tables
107 #define FSTR_PTR(_struct) &_##_struct.fstr
108 
126 #define FSTR_TABLE(_name) const FlashString* const _name[] PROGMEM
127 
128 /*
129  * Load a FlashString object into a named local (stack) buffer
130  *
131  * For example:
132  *
133  * DEFINE_FSTR(globalTest, "This is a testing string")
134  * ...
135  * LOAD_FSTR(local, globalTest)
136  * printf("%s, %u characters, buffer is %u bytes\n", local, globalTest.length, sizeof(local));
137  */
138 #define LOAD_FSTR(_name, _fstr) \
139  char _name[(_fstr).size()] __attribute__((aligned(4))); \
140  memcpy_aligned(_name, (_fstr).data(), sizeof(_name));
141 
142 /*
143  * Define a flash string and load it into a named char[] buffer on the stack.
144  * This allows sizeof(_name) to work as if the string were defined thus:
145  *
146  * char _name[] = "text";
147  */
148 #define FSTR_ARRAY(_name, _str) \
149  static DEFINE_FSTR(_##_name, _str); \
150  LOAD_FSTR(_name, _##_name)
151 
157 #define IMPORT_FSTR(name, file) \
158  __asm__(".section .irom.text\n" \
159  ".global " #name "\n" \
160  ".type " #name ", @object\n" \
161  ".align 4\n" #name ":\n" \
162  ".word _" #name "_end - " #name " - 4\n" \
163  ".incbin \"" file "\"\n" \
164  "_" #name "_end:\n"); \
165  extern const __attribute__((aligned(4))) FlashString name;
166 
171 struct FlashString {
172  // Do NOT access these directly - use member functions
173  uint32_t flashLength;
174  char flashData[];
175 
176  uint32_t length() const
177  {
178  return flashLength;
179  }
180 
181  uint32_t size() const
182  {
183  return ALIGNUP(flashLength + 1);
184  }
185 
186  flash_string_t data() const
187  {
188  return FPSTR(flashData);
189  }
190 
196  bool isEqual(const char* cstr) const
197  {
198  // Unlikely we'd want an empty flash string, but check anyway
199  if(cstr == nullptr)
200  return flashLength == 0;
201  // Don't use strcmp as our data may contain nuls
202  size_t cstrlen = strlen(cstr);
203  if(cstrlen != flashLength)
204  return false;
205  LOAD_FSTR(buf, *this);
206  return memcmp(buf, cstr, cstrlen) == 0;
207  }
208 
213  bool isEqual(const FlashString& str) const
214  {
215  if(flashLength != str.flashLength)
216  return false;
217  if(flashData == str.flashData)
218  return true;
219  return memcmp_aligned(flashData, str.flashData, ALIGNUP(flashLength)) == 0;
220  }
221 
222  bool isEqual(const String& str) const
223  {
224  return str.equals(*this);
225  }
226 
227  bool operator==(const char* str) const
228  {
229  return isEqual(str);
230  }
231 
232  bool operator==(const FlashString& str) const
233  {
234  return isEqual(str);
235  }
236 
237  bool operator==(const String& str) const
238  {
239  return isEqual(str);
240  }
241 
242  bool operator!=(const char* str) const
243  {
244  return !isEqual(str);
245  }
246 
247  bool operator!=(const FlashString& str) const
248  {
249  return !isEqual(str);
250  }
251 
252  bool operator!=(const String& str) const
253  {
254  return !isEqual(str);
255  }
256 };
257 
258 #endif /* __FLASH_STRING_H_ */
bool isEqual(const char *cstr) const
Check for equality with a C-string.
Definition: FlashString.h:196
uint32_t flashLength
Only needs to be uint16_t but ensures data is aligned.
Definition: FlashString.h:173
describes a counted string stored in flash memory
Definition: FlashString.h:171
The string class.
Definition: WString.h:104
bool isEqual(const FlashString &str) const
Check for equality with another FlashString.
Definition: FlashString.h:213