Commit ab309e40 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by Me No Dev

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.
parent 932666a0
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
StreamString.cpp StreamString.cpp
Copyright (c) 2015 Markus Sattler. All rights reserved. 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 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
...@@ -22,31 +23,27 @@ ...@@ -22,31 +23,27 @@
#include <Arduino.h> #include <Arduino.h>
#include "StreamString.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(size && data) {
if(reserve(length() + size + 1)) { if(reserve(length() + size + 1)) {
memcpy((void *) (buffer + len), (const void *) data, size); memcpy((void *) (wbuffer() + len()), (const void *) data, size);
len += size; setLen(len() + size);
*(buffer + len) = 0x00; // add null for string end *(wbuffer() + len()) = 0x00; // add null for string end
return size; return size;
} }
} }
return 0; return 0;
} }
size_t StreamString::write(uint8_t data) size_t StreamString::write(uint8_t data) {
{
return concat((char) data); return concat((char) data);
} }
int StreamString::available() int StreamString::available() {
{
return length(); return length();
} }
int StreamString::read() int StreamString::read() {
{
if(length()) { if(length()) {
char c = charAt(0); char c = charAt(0);
remove(0, 1); remove(0, 1);
...@@ -56,8 +53,7 @@ int StreamString::read() ...@@ -56,8 +53,7 @@ int StreamString::read()
return -1; return -1;
} }
int StreamString::peek() int StreamString::peek() {
{
if(length()) { if(length()) {
char c = charAt(0); char c = charAt(0);
return c; return c;
...@@ -65,7 +61,6 @@ int StreamString::peek() ...@@ -65,7 +61,6 @@ int StreamString::peek()
return -1; return -1;
} }
void StreamString::flush() void StreamString::flush() {
{
} }
This diff is collapsed.
...@@ -35,20 +35,19 @@ class StringSumHelper; ...@@ -35,20 +35,19 @@ class StringSumHelper;
// an abstract class used as a means to proide a unique pointer type // an abstract class used as a means to proide a unique pointer type
// but really has no body // but really has no body
class __FlashStringHelper; 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 // The string class
class String class String {
{
// use a function pointer to allow for "if (s)" without the // use a function pointer to allow for "if (s)" without the
// complications of an operator bool(). for more information, see: // complications of an operator bool(). for more information, see:
// http://www.artima.com/cppsource/safebool.html // http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const; typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const void StringIfHelper() const {
{
} }
public: public:
// constructors // constructors
// creates a copy of the initial value. // creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation // if the initial value is null or invalid, or if memory allocation
...@@ -56,7 +55,7 @@ public: ...@@ -56,7 +55,7 @@ public:
// be false). // be false).
String(const char *cstr = ""); String(const char *cstr = "");
String(const String &str); String(const String &str);
String(const __FlashStringHelper *str) : String(reinterpret_cast<const char *>(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);
...@@ -76,10 +75,9 @@ public: ...@@ -76,10 +75,9 @@ public:
// is left unchanged). reserve(0), if successful, will validate an // is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards) // invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size); unsigned char reserve(unsigned int size);
inline unsigned int length(void) const inline unsigned int length(void) const {
{ if(buffer()) {
if(buffer) { return len();
return len;
} else { } else {
return 0; return 0;
} }
...@@ -115,58 +113,47 @@ public: ...@@ -115,58 +113,47 @@ public:
// 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)
String & operator +=(const String &rhs) String & operator +=(const String &rhs) {
{
concat(rhs); concat(rhs);
return (*this); return (*this);
} }
String & operator +=(const char *cstr) String & operator +=(const char *cstr) {
{
concat(cstr); concat(cstr);
return (*this); return (*this);
} }
String & operator +=(char c) String & operator +=(char c) {
{
concat(c); concat(c);
return (*this); return (*this);
} }
String & operator +=(unsigned char num) String & operator +=(unsigned char num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(int num) String & operator +=(int num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(unsigned int num) String & operator +=(unsigned int num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(long num) String & operator +=(long num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(unsigned long num) String & operator +=(unsigned long num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(float num) String & operator +=(float num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(double num) String & operator +=(double num) {
{
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator += (const __FlashStringHelper *str) String & operator += (const __FlashStringHelper *str){
{
concat(str); concat(str);
return (*this); return (*this);
} }
...@@ -184,27 +171,22 @@ public: ...@@ -184,27 +171,22 @@ public:
friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs); 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 {
{ return buffer() ? &String::StringIfHelper : 0;
return buffer ? &String::StringIfHelper : 0;
} }
int compareTo(const String &s) const; int compareTo(const String &s) const;
unsigned char equals(const String &s) const; unsigned char equals(const String &s) const;
unsigned char equals(const char *cstr) 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); return equals(rhs);
} }
unsigned char operator ==(const char *cstr) const unsigned char operator ==(const char *cstr) const {
{
return equals(cstr); return equals(cstr);
} }
unsigned char operator !=(const String &rhs) const unsigned char operator !=(const String &rhs) const {
{
return !equals(rhs); return !equals(rhs);
} }
unsigned char operator !=(const char *cstr) const unsigned char operator !=(const char *cstr) const {
{
return !equals(cstr); return !equals(cstr);
} }
unsigned char operator <(const String &rhs) const; unsigned char operator <(const String &rhs) const;
...@@ -223,14 +205,14 @@ public: ...@@ -223,14 +205,14 @@ public:
char operator [](unsigned int index) const; char operator [](unsigned int index) const;
char& operator [](unsigned int index); char& operator [](unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const; 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); getBytes((unsigned char *) buf, bufsize, index);
} }
const char * c_str() const const char* c_str() const { return buffer(); }
{ char* begin() { return wbuffer(); }
return buffer; char* end() { return wbuffer() + length(); }
} const char* begin() const { return c_str(); }
const char* end() const { return c_str() + length(); }
// search // search
int indexOf(char ch) const; int indexOf(char ch) const;
...@@ -241,9 +223,8 @@ public: ...@@ -241,9 +223,8 @@ public:
int lastIndexOf(char ch, unsigned int fromIndex) const; int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const; int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const; int lastIndexOf(const String &str, unsigned int fromIndex) const;
String substring(unsigned int beginIndex) const String substring(unsigned int beginIndex) const {
{ return substring(beginIndex, len());
return substring(beginIndex, len);
} }
; ;
String substring(unsigned int beginIndex, unsigned int endIndex) const; String substring(unsigned int beginIndex, unsigned int endIndex) const;
...@@ -262,11 +243,36 @@ public: ...@@ -262,11 +243,36 @@ public:
float toFloat(void) const; float toFloat(void) const;
double toDouble(void) const; double toDouble(void) const;
protected: protected:
char *buffer; // the actual char array // Contains the string info when we're not in SSO mode
unsigned int capacity; // the array length minus one (for the '\0') struct _ptr {
unsigned int len; // the String length (not counting the '\0') char * buff;
protected: 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 init(void);
void invalidate(void); void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen); unsigned char changeBuffer(unsigned int maxStrLen);
...@@ -280,50 +286,41 @@ protected: ...@@ -280,50 +286,41 @@ protected:
#endif #endif
}; };
class StringSumHelper: public String class StringSumHelper: public String {
{ public:
public:
StringSumHelper(const String &s) : StringSumHelper(const String &s) :
String(s) String(s) {
{
} }
StringSumHelper(const char *p) : StringSumHelper(const char *p) :
String(p) String(p) {
{
} }
StringSumHelper(char c) : StringSumHelper(char c) :
String(c) String(c) {
{
} }
StringSumHelper(unsigned char num) : StringSumHelper(unsigned char num) :
String(num) String(num) {
{
} }
StringSumHelper(int num) : StringSumHelper(int num) :
String(num) String(num) {
{
} }
StringSumHelper(unsigned int num) : StringSumHelper(unsigned int num) :
String(num) String(num) {
{
} }
StringSumHelper(long num) : StringSumHelper(long num) :
String(num) String(num) {
{
} }
StringSumHelper(unsigned long num) : StringSumHelper(unsigned long num) :
String(num) String(num) {
{
} }
StringSumHelper(float num) : StringSumHelper(float num) :
String(num) String(num) {
{
} }
StringSumHelper(double num) : StringSumHelper(double num) :
String(num) String(num) {
{
} }
}; };
extern const String emptyString;
#endif // __cplusplus #endif // __cplusplus
#endif // String_class_h #endif // String_class_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment