Copy ESP8266 String w/SSO to ESP32 repo (#2715)

I redid the ESP8266 WString library to enable small string optimization
(SSO) a while back, and think it would be helpful even on the ESP32 with
its higher memory complement.

SSO avoids lots of tiny mallocs() on the heap which cause fragmentation
by using the memory in the class object itself to store the actual
string and only mallocing() for buffers that are larger than what can
fit in thie class object.  Modern C++ std::string implementations have
this optimization as well, but since we're using Arduino strings we had
to roll our own.
This commit is contained in:
Earle F. Philhower, III 2019-04-26 09:41:42 -07:00 committed by Me No Dev
parent 932666a03f
commit ab309e40d5
3 changed files with 797 additions and 870 deletions

View File

@ -2,6 +2,7 @@
StreamString.cpp
Copyright (c) 2015 Markus Sattler. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -22,31 +23,27 @@
#include <Arduino.h>
#include "StreamString.h"
size_t StreamString::write(const uint8_t *data, size_t size)
{
size_t StreamString::write(const uint8_t *data, size_t size) {
if(size && data) {
if(reserve(length() + size + 1)) {
memcpy((void *) (buffer + len), (const void *) data, size);
len += size;
*(buffer + len) = 0x00; // add null for string end
memcpy((void *) (wbuffer() + len()), (const void *) data, size);
setLen(len() + size);
*(wbuffer() + len()) = 0x00; // add null for string end
return size;
}
}
return 0;
}
size_t StreamString::write(uint8_t data)
{
size_t StreamString::write(uint8_t data) {
return concat((char) data);
}
int StreamString::available()
{
int StreamString::available() {
return length();
}
int StreamString::read()
{
int StreamString::read() {
if(length()) {
char c = charAt(0);
remove(0, 1);
@ -56,8 +53,7 @@ int StreamString::read()
return -1;
}
int StreamString::peek()
{
int StreamString::peek() {
if(length()) {
char c = charAt(0);
return c;
@ -65,7 +61,6 @@ int StreamString::peek()
return -1;
}
void StreamString::flush()
{
void StreamString::flush() {
}

File diff suppressed because it is too large Load Diff

View File

@ -35,20 +35,19 @@ class StringSumHelper;
// an abstract class used as a means to proide a unique pointer type
// but really has no body
class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
// The string class
class String
{
class String {
// use a function pointer to allow for "if (s)" without the
// complications of an operator bool(). for more information, see:
// http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const
{
void StringIfHelper() const {
}
public:
public:
// constructors
// creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation
@ -56,7 +55,7 @@ public:
// be false).
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str) : String(reinterpret_cast<const char *>(str)) {};
String(const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String(String &&rval);
String(StringSumHelper &&rval);
@ -76,10 +75,9 @@ public:
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const
{
if(buffer) {
return len;
inline unsigned int length(void) const {
if(buffer()) {
return len();
} else {
return 0;
}
@ -115,58 +113,47 @@ public:
// if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way)
String & operator +=(const String &rhs)
{
String & operator +=(const String &rhs) {
concat(rhs);
return (*this);
}
String & operator +=(const char *cstr)
{
String & operator +=(const char *cstr) {
concat(cstr);
return (*this);
}
String & operator +=(char c)
{
String & operator +=(char c) {
concat(c);
return (*this);
}
String & operator +=(unsigned char num)
{
String & operator +=(unsigned char num) {
concat(num);
return (*this);
}
String & operator +=(int num)
{
String & operator +=(int num) {
concat(num);
return (*this);
}
String & operator +=(unsigned int num)
{
String & operator +=(unsigned int num) {
concat(num);
return (*this);
}
String & operator +=(long num)
{
String & operator +=(long num) {
concat(num);
return (*this);
}
String & operator +=(unsigned long num)
{
String & operator +=(unsigned long num) {
concat(num);
return (*this);
}
String & operator +=(float num)
{
String & operator +=(float num) {
concat(num);
return (*this);
}
String & operator +=(double num)
{
String & operator +=(double num) {
concat(num);
return (*this);
}
String & operator += (const __FlashStringHelper *str)
{
String & operator += (const __FlashStringHelper *str){
concat(str);
return (*this);
}
@ -184,27 +171,22 @@ public:
friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
// comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const
{
return buffer ? &String::StringIfHelper : 0;
operator StringIfHelperType() const {
return buffer() ? &String::StringIfHelper : 0;
}
int compareTo(const String &s) const;
unsigned char equals(const String &s) const;
unsigned char equals(const char *cstr) const;
unsigned char operator ==(const String &rhs) const
{
unsigned char operator ==(const String &rhs) const {
return equals(rhs);
}
unsigned char operator ==(const char *cstr) const
{
unsigned char operator ==(const char *cstr) const {
return equals(cstr);
}
unsigned char operator !=(const String &rhs) const
{
unsigned char operator !=(const String &rhs) const {
return !equals(rhs);
}
unsigned char operator !=(const char *cstr) const
{
unsigned char operator !=(const char *cstr) const {
return !equals(cstr);
}
unsigned char operator <(const String &rhs) const;
@ -223,14 +205,14 @@ public:
char operator [](unsigned int index) const;
char& operator [](unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const
{
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
getBytes((unsigned char *) buf, bufsize, index);
}
const char * c_str() const
{
return buffer;
}
const char* c_str() const { return buffer(); }
char* begin() { return wbuffer(); }
char* end() { return wbuffer() + length(); }
const char* begin() const { return c_str(); }
const char* end() const { return c_str() + length(); }
// search
int indexOf(char ch) const;
@ -241,9 +223,8 @@ public:
int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const;
String substring(unsigned int beginIndex) const
{
return substring(beginIndex, len);
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
;
String substring(unsigned int beginIndex, unsigned int endIndex) const;
@ -262,11 +243,36 @@ public:
float toFloat(void) const;
double toDouble(void) const;
protected:
char *buffer; // the actual char array
unsigned int capacity; // the array length minus one (for the '\0')
unsigned int len; // the String length (not counting the '\0')
protected:
protected:
// Contains the string info when we're not in SSO mode
struct _ptr {
char * buff;
uint16_t cap;
uint16_t len;
};
// SSO is handled by checking the last byte of sso_buff.
// When not in SSO mode, that byte is set to 0xff, while when in SSO mode it is always 0x00 (so it can serve as the string terminator as well as a flag)
// This allows strings up up to 12 (11 + \0 termination) without any extra space.
enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more
enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum
union {
struct _ptr ptr;
char sso_buf[SSOSIZE];
};
// Accessor functions
inline bool sso() const { return sso_buf[SSOSIZE - 1] == 0; }
inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; }
inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; }
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
inline void setLen(int len) { if (!sso()) ptr.len = len; }
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
// Buffer accessor functions
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); }
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buff; } // Writable version of buffer
protected:
void init(void);
void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
@ -280,50 +286,41 @@ protected:
#endif
};
class StringSumHelper: public String
{
public:
class StringSumHelper: public String {
public:
StringSumHelper(const String &s) :
String(s)
{
String(s) {
}
StringSumHelper(const char *p) :
String(p)
{
String(p) {
}
StringSumHelper(char c) :
String(c)
{
String(c) {
}
StringSumHelper(unsigned char num) :
String(num)
{
String(num) {
}
StringSumHelper(int num) :
String(num)
{
String(num) {
}
StringSumHelper(unsigned int num) :
String(num)
{
String(num) {
}
StringSumHelper(long num) :
String(num)
{
String(num) {
}
StringSumHelper(unsigned long num) :
String(num)
{
String(num) {
}
StringSumHelper(float num) :
String(num)
{
String(num) {
}
StringSumHelper(double num) :
String(num)
{
String(num) {
}
};
extern const String emptyString;
#endif // __cplusplus
#endif // String_class_h