1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

PgmSpace working

PSTR() and F() macros correctly place string into flash memory relying
on PROGMEM
PROGMEM uses ICACHE_RODATA_ATTR
Print and String classes fixed up
str* classes fixed up
This commit is contained in:
Makuna 2015-05-13 11:27:54 -07:00
parent b959e82165
commit 81d27b403e
7 changed files with 295 additions and 29 deletions

View File

@ -18,6 +18,7 @@
Modified 23 November 2006 by David A. Mellis Modified 23 November 2006 by David A. Mellis
Modified December 2014 by Ivan Grokhotkov Modified December 2014 by Ivan Grokhotkov
Modified May 2015 by Michael C. Miller - esp8266 progmem support
*/ */
#include <stdlib.h> #include <stdlib.h>
@ -42,6 +43,18 @@ size_t ICACHE_FLASH_ATTR Print::write(const uint8_t *buffer, size_t size) {
return n; return n;
} }
size_t ICACHE_FLASH_ATTR Print::print(const __FlashStringHelper *ifsh) {
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
size_t n = 0;
while (1) {
uint8_t c = pgm_read_byte(p++);
if (c == 0) break;
n += write(c);
}
return n;
}
size_t ICACHE_FLASH_ATTR Print::print(const String &s) { size_t ICACHE_FLASH_ATTR Print::print(const String &s) {
return write(s.c_str(), s.length()); return write(s.c_str(), s.length());
} }
@ -92,6 +105,12 @@ size_t ICACHE_FLASH_ATTR Print::print(double n, int digits) {
return printFloat(n, digits); return printFloat(n, digits);
} }
size_t ICACHE_FLASH_ATTR Print::println(const __FlashStringHelper *ifsh) {
size_t n = print(ifsh);
n += println();
return n;
}
size_t ICACHE_FLASH_ATTR Print::print(const Printable& x) { size_t ICACHE_FLASH_ATTR Print::print(const Printable& x) {
return x.printTo(*this); return x.printTo(*this);
} }

View File

@ -63,6 +63,7 @@ class Print {
return write((const uint8_t *) buffer, size); return write((const uint8_t *) buffer, size);
} }
size_t print(const __FlashStringHelper *);
size_t print(const String &); size_t print(const String &);
size_t print(const char[]); size_t print(const char[]);
size_t print(char); size_t print(char);
@ -74,6 +75,7 @@ class Print {
size_t print(double, int = 2); size_t print(double, int = 2);
size_t print(const Printable&); size_t print(const Printable&);
size_t println(const __FlashStringHelper *);
size_t println(const String &s); size_t println(const String &s);
size_t println(const char[]); size_t println(const char[]);
size_t println(char); size_t println(char);

View File

@ -4,6 +4,7 @@
Copyright (c) 2009-10 Hernando Barragan. All rights reserved. Copyright (c) 2009-10 Hernando Barragan. All rights reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com Copyright 2011, Paul Stoffregen, paul@pjrc.com
Modified by Ivan Grokhotkov, 2014 - esp8266 support Modified by Ivan Grokhotkov, 2014 - esp8266 support
Modified by Michael C. Miller, 2015 - esp8266 progmem support
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -44,6 +45,11 @@ ICACHE_FLASH_ATTR String::String(const String &value) {
*this = value; *this = value;
} }
ICACHE_FLASH_ATTR String::String(const __FlashStringHelper *pstr) {
init();
*this = pstr; // see operator =
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
ICACHE_FLASH_ATTR String::String(String &&rval) { ICACHE_FLASH_ATTR String::String(String &&rval) {
init(); init();
@ -167,6 +173,16 @@ String & ICACHE_FLASH_ATTR String::copy(const char *cstr, unsigned int length) {
return *this; return *this;
} }
String & ICACHE_FLASH_ATTR String::copy(const __FlashStringHelper *pstr, unsigned int length) {
if (!reserve(length)) {
invalidate();
return *this;
}
len = length;
strcpy_P(buffer, (PGM_P)pstr);
return *this;
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
void ICACHE_FLASH_ATTR String::move(String &rhs) { void ICACHE_FLASH_ATTR String::move(String &rhs) {
if(buffer) { if(buffer) {
@ -223,6 +239,14 @@ String & ICACHE_FLASH_ATTR String::operator =(const char *cstr) {
return *this; return *this;
} }
String & ICACHE_FLASH_ATTR String::operator = (const __FlashStringHelper *pstr)
{
if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
else invalidate();
return *this;
}
// /*********************************************/ // /*********************************************/
// /* concat */ // /* concat */
// /*********************************************/ // /*********************************************/
@ -299,6 +323,17 @@ unsigned char ICACHE_FLASH_ATTR String::concat(double num) {
return concat(string, strlen(string)); return concat(string, strlen(string));
} }
unsigned char ICACHE_FLASH_ATTR String::concat(const __FlashStringHelper * str) {
if (!str) return 0;
int length = strlen_P((PGM_P)str);
if (length == 0) return 1;
unsigned int newlen = len + length;
if (!reserve(newlen)) return 0;
strcpy_P(buffer + len, (PGM_P)str);
len = newlen;
return 1;
}
/*********************************************/ /*********************************************/
/* Concatenate */ /* Concatenate */
/*********************************************/ /*********************************************/
@ -373,6 +408,13 @@ StringSumHelper & ICACHE_FLASH_ATTR operator +(const StringSumHelper &lhs, doubl
return a; return a;
} }
StringSumHelper & ICACHE_FLASH_ATTR operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(rhs)) a.invalidate();
return a;
}
// /*********************************************/ // /*********************************************/
// /* Comparison */ // /* Comparison */
// /*********************************************/ // /*********************************************/

View File

@ -26,15 +26,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#define PROGMEM #include <pgmspace.h>
// An inherited class for holding the result of a concatenation. These // An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations. // result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper; class StringSumHelper;
typedef char* __FlashStringHelper; // an abstract class used as a means to proide a unique pointer type
//#define F(str) []() -> const char * { static const char tmp[] ICACHE_RODATA_ATTR = str; return &tmp[0]; }() // but really has no body
#define F(str) str class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
// The string class // The string class
class String { class String {
@ -53,6 +55,7 @@ class String {
// be false). // be false).
String(const char *cstr = ""); String(const char *cstr = "");
String(const String &str); String(const String &str);
String(const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
String(String &&rval); String(String &&rval);
String(StringSumHelper &&rval); String(StringSumHelper &&rval);
@ -81,6 +84,7 @@ class String {
// marked as invalid ("if (s)" will be false). // marked as invalid ("if (s)" will be false).
String & operator =(const String &rhs); String & operator =(const String &rhs);
String & operator =(const char *cstr); String & operator =(const char *cstr);
String & operator = (const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
String & operator =(String &&rval); String & operator =(String &&rval);
String & operator =(StringSumHelper &&rval); String & operator =(StringSumHelper &&rval);
@ -101,6 +105,7 @@ class String {
unsigned char concat(unsigned long num); unsigned char concat(unsigned long num);
unsigned char concat(float num); unsigned char concat(float num);
unsigned char concat(double num); unsigned char concat(double num);
unsigned char concat(const __FlashStringHelper * str);
// if there's not enough memory for the concatenated value, the string // if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way) // will be left unchanged (but this isn't signalled in any way)
@ -144,6 +149,10 @@ class String {
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator += (const __FlashStringHelper *str){
concat(str);
return (*this);
}
friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs); friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr); friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr);
@ -155,6 +164,7 @@ class String {
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, float num); friend StringSumHelper & operator +(const StringSumHelper &lhs, float num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, double num); friend StringSumHelper & operator +(const StringSumHelper &lhs, double num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
// comparison (only works w/ Strings and "strings") // comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const { operator StringIfHelperType() const {
@ -237,6 +247,7 @@ class String {
// copy and move // copy and move
String & copy(const char *cstr, unsigned int length); String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *pstr, unsigned int length);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
void move(String &rhs); void move(String &rhs);
#endif #endif

View File

@ -116,7 +116,7 @@ char* strncpy(char * dest, const char * src, size_t n) {
return ets_strncpy(dest, src, n); return ets_strncpy(dest, src, n);
} }
size_t strnlen(const char *s, size_t len) { size_t ICACHE_FLASH_ATTR strnlen(const char *s, size_t len) {
// there is no ets_strnlen // there is no ets_strnlen
const char *cp; const char *cp;
for (cp = s; len != 0 && *cp != '\0'; cp++, len--); for (cp = s; len != 0 && *cp != '\0'; cp++, len--);
@ -127,7 +127,7 @@ char* strstr(const char *haystack, const char *needle) {
return ets_strstr(haystack, needle); return ets_strstr(haystack, needle);
} }
char* strchr(const char * str, int character) { char* ICACHE_FLASH_ATTR strchr(const char * str, int character) {
while(1) { while(1) {
if(*str == 0x00) { if(*str == 0x00) {
return NULL; return NULL;
@ -139,7 +139,7 @@ char* strchr(const char * str, int character) {
} }
} }
char * strrchr(const char * str, int character) { char * ICACHE_FLASH_ATTR strrchr(const char * str, int character) {
char * ret = NULL; char * ret = NULL;
while(1) { while(1) {
if(*str == 0x00) { if(*str == 0x00) {
@ -223,7 +223,7 @@ char* ICACHE_FLASH_ATTR strtok(char * str, const char * delimiters) {
return ret; return ret;
} }
int strcasecmp(const char * str1, const char * str2) { int ICACHE_FLASH_ATTR strcasecmp(const char * str1, const char * str2) {
int d = 0; int d = 0;
while(1) { while(1) {
int c1 = tolower(*str1++); int c1 = tolower(*str1++);

161
cores/esp8266/pgmspace.cpp Normal file
View File

@ -0,0 +1,161 @@
/*
pgmspace.cpp - string functions that support PROGMEM
Copyright (c) 2015 Michael C. Miller. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <ctype.h>
#include "pgmspace.h"
size_t ICACHE_FLASH_ATTR strnlen_P(const char* s, size_t size) {
const char* cp;
for (cp = s; size != 0 && pgm_read_byte(cp) != '\0'; cp++, size--);
return (size_t)(cp - s);
}
void* ICACHE_FLASH_ATTR memcpy_P(void* dest, const void* src, size_t count) {
const uint8_t* read = reinterpret_cast<const uint8_t*>(src);
uint8_t* write = reinterpret_cast<uint8_t*>(dest);
while (count)
{
*write++ = pgm_read_byte(read++);
count--;
}
return dest;
}
char* ICACHE_FLASH_ATTR strncpy_P(char* dest, const char* src, size_t size) {
const char* read = src;
char* write = dest;
char ch = '.';
while (size > 0 && ch != '\0')
{
ch = pgm_read_byte(read++);
*write++ = ch;
size--;
}
return dest;
}
char* ICACHE_FLASH_ATTR strncat_P(char* dest, const char* src, size_t size) {
char* write = dest;
while (*write != '\0')
{
write++;
}
const char* read = src;
char ch = '.';
while (size > 0 && ch != '\0')
{
ch = pgm_read_byte(read++);
*write++ = ch;
size--;
}
if (ch != '\0')
{
*write = '\0';
}
return dest;
}
int ICACHE_FLASH_ATTR strncmp_P(const char* str1, const char* str2P, size_t size) {
int result = 0;
while (size > 0)
{
char ch1 = *str1++;
char ch2 = pgm_read_byte(str2P++);
result = ch1 - ch2;
if (result != 0 || ch2 == '\0')
{
break;
}
size--;
}
return result;
}
int ICACHE_FLASH_ATTR strncasecmp_P(const char* str1, const char* str2P, size_t size) {
int result = 0;
while (size > 0)
{
char ch1 = tolower(*str1++);
char ch2 = tolower(pgm_read_byte(str2P++));
result = ch1 - ch2;
if (result != 0 || ch2 == '\0')
{
break;
}
size--;
}
return result;
}
int ICACHE_FLASH_ATTR printf_P(const char* formatP, ...) {
int ret;
va_list arglist;
va_start(arglist, formatP);
size_t fmtLen = strlen_P(formatP);
char* format = new char[fmtLen + 1];
strcpy_P(format, formatP);
ret = os_printf(format, arglist);
delete [] format;
va_end(arglist);
return ret;
}
int ICACHE_FLASH_ATTR snprintf_P(char* str, size_t strSize, const char* formatP, ...) {
int ret;
va_list arglist;
va_start(arglist, formatP);
ret = vsnprintf_P(str, strSize, formatP, arglist);
va_end(arglist);
return ret;
}
int ICACHE_FLASH_ATTR vsnprintf_P(char* str, size_t strSize, const char* formatP, va_list ap) {
int ret;
size_t fmtLen = strlen_P(formatP);
char* format = new char[fmtLen + 1];
strcpy_P(format, formatP);
ret = ets_vsnprintf(str, strSize, format, ap);
delete [] format;
return ret;
}

View File

@ -12,13 +12,11 @@ extern "C" {
} }
#endif #endif
#define PROGMEM #define PROGMEM ICACHE_RODATA_ATTR
#define PGM_P const char * #define PGM_P const char *
#define PSTR(str) (str) #define PGM_VOID_P const void *
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#define vsnprintf_P(...) ets_vsnprintf( __VA_ARGS__ )
#define snprintf_P(...) snprintf( __VA_ARGS__ )
#define printf_P(...) os_printf(__VA_ARGS__)
#define _SFR_BYTE(n) (n) #define _SFR_BYTE(n) (n)
@ -32,23 +30,56 @@ typedef uint16_t prog_uint16_t;
typedef int32_t prog_int32_t; typedef int32_t prog_int32_t;
typedef uint32_t prog_uint32_t; typedef uint32_t prog_uint32_t;
#define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) #define SIZE_IRRELEVANT 0x7fffffff
#define strcpy_P(dest, src) strcpy((dest), (src))
#define strcat_P(dest, src) strcat((dest), (src))
#define strcmp_P(a, b) strcmp((a), (b))
#define strstr_P(a, b) strstr((a), (b))
#define strlen_P(s) strlen((const char *)(s))
#define strcasecmp_P(a, b) strcasecmp((a), (b))
#define strncpy_P(dest, src, size) strncpy((dest), (src), (size))
#define strncat_P(dest, src, size) strncat((dest), (src), (size))
#define strncmp_P(a, b, size) strncmp((a), (b), (size))
#define strnlen_P(s, size) strnlen((const char *)(s), (size))
#define strncasecmp_P(a, b, size) strncasecmp((a), (b), (size))
#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) extern void* memcpy_P(void* dest, const void* src, size_t count);
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
#define pgm_read_dword(addr) (*(const unsigned long *)(addr)) extern char* strncpy_P(char* dest, const char* src, size_t size);
#define pgm_read_float(addr) (*(const float *)(addr)) #define strcpy_P(dest, src) strncpy_P((dest), (src), SIZE_IRRELEVANT)
extern char* strncat_P(char* dest, const char* src, size_t size);
#define strcat_P(dest, src) strncat_P((dest), (src), SIZE_IRRELEVANT)
extern int strncmp_P(const char* str1, const char* str2P, size_t size);
#define strcmp_P(str1, str2P) strncmp_P((str1), (str2P), SIZE_IRRELEVANT)
extern int strncasecmp_P(const char* str1, const char* str2P, size_t size);
#define strcasecmp_P(str1, str2P) strncasecmp_P((str1), (str2P), SIZE_IRRELEVANT)
extern size_t strnlen_P(const char *s, size_t size);
#define strlen_P(strP) strnlen_P((strP), SIZE_IRRELEVANT)
extern int printf_P(const char *formatP, ...);
extern int snprintf_P(char *str, size_t strSize, const char *formatP, ...);
extern int vsnprintf_P(char *str, size_t strSize, const char *formatP, va_list ap);
// flash memory must be read using 32 bit aligned addresses else a processor
// exception will be triggered
// order within the 32 bit values are
// --------------
// b3, b2, b1, b0
// w1, w0
#define pgm_read_byte(addr) \
(__extension__({ \
PGM_P __local = (PGM_P)(addr); /* isolate varible for macro expansion */ \
ptrdiff_t __offset = ((uint32_t)__local & 0x00000003); /* byte aligned mask */ \
const uint32_t* __addr32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(__local)-__offset); \
uint8_t __result = ((*__addr32) >> (__offset * 8)); \
__result; \
}))
#define pgm_read_word(addr) \
(__extension__({ \
PGM_P __local = (PGM_P)(addr); /* isolate varible for macro expansion */ \
ptrdiff_t __offset = ((uint32_t)__local & 0x00000002); /* word aligned mask */ \
const uint32_t* __addr32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(__local) - __offset); \
uint16_t __result = ((*__addr32) >> (__offset * 8)); \
__result; \
}))
#define pgm_read_dword(addr) (*reinterpret_cast<const uint32_t*>(addr))
#define pgm_read_float(addr) (*reinterpret_cast<const float*>(addr))
#define pgm_read_byte_near(addr) pgm_read_byte(addr) #define pgm_read_byte_near(addr) pgm_read_byte(addr)
#define pgm_read_word_near(addr) pgm_read_word(addr) #define pgm_read_word_near(addr) pgm_read_word(addr)