From a1870437c8ac8604f1eb746dbd08745c05b0f90c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 09:55:11 +0100 Subject: [PATCH 1/9] Remove strlen in calls like String::concat(s, strlen(s)) Instead of calling strlen in a dozen places, instead just call String::concat(s), which will then call strlen. This shrinks the code size of these calls significantly, the StringAppendOperator example on the Arduino Uno shrinks by 72 bytes. This change does incur a slight runtime cost, because there is one extra function call. --- hardware/arduino/avr/cores/arduino/WString.cpp | 16 ++++++++-------- hardware/arduino/sam/cores/arduino/WString.cpp | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/WString.cpp b/hardware/arduino/avr/cores/arduino/WString.cpp index cd3e0e876d0..956df40337d 100644 --- a/hardware/arduino/avr/cores/arduino/WString.cpp +++ b/hardware/arduino/avr/cores/arduino/WString.cpp @@ -289,49 +289,49 @@ unsigned char String::concat(unsigned char num) { char buf[1 + 3 * sizeof(unsigned char)]; itoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(int num) { char buf[2 + 3 * sizeof(int)]; itoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(unsigned int num) { char buf[1 + 3 * sizeof(unsigned int)]; utoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(long num) { char buf[2 + 3 * sizeof(long)]; ltoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(unsigned long num) { char buf[1 + 3 * sizeof(unsigned long)]; ultoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(float num) { char buf[20]; char* string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); + return concat(string); } unsigned char String::concat(double num) { char buf[20]; char* string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); + return concat(string); } unsigned char String::concat(const __FlashStringHelper * str) @@ -360,7 +360,7 @@ StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs) StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr) { StringSumHelper &a = const_cast(lhs); - if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate(); + if (!cstr || !a.concat(cstr)) a.invalidate(); return a; } diff --git a/hardware/arduino/sam/cores/arduino/WString.cpp b/hardware/arduino/sam/cores/arduino/WString.cpp index e1d60f471fa..2311315ad49 100644 --- a/hardware/arduino/sam/cores/arduino/WString.cpp +++ b/hardware/arduino/sam/cores/arduino/WString.cpp @@ -291,49 +291,49 @@ unsigned char String::concat(unsigned char num) { char buf[1 + 3 * sizeof(unsigned char)]; itoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(int num) { char buf[2 + 3 * sizeof(int)]; itoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(unsigned int num) { char buf[1 + 3 * sizeof(unsigned int)]; utoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(long num) { char buf[2 + 3 * sizeof(long)]; ltoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(unsigned long num) { char buf[1 + 3 * sizeof(unsigned long)]; ultoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(buf); } unsigned char String::concat(float num) { char buf[20]; char* string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); + return concat(string); } unsigned char String::concat(double num) { char buf[20]; char* string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); + return concat(string); } unsigned char String::concat(const __FlashStringHelper * str) @@ -362,7 +362,7 @@ StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs) StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr) { StringSumHelper &a = const_cast(lhs); - if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate(); + if (!cstr || !a.concat(cstr)) a.invalidate(); return a; } From c63b861241eed1340ad56c0d05be9b65b90c91cb Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 09:58:47 +0100 Subject: [PATCH 2/9] Make string concat, copy and move work on non-terminated strings Previously, these methods took a nul-terminated string and appended it to the current buffer. The length argument (or length of the passed String object) was used to allocate memory, but was not used when actually copying the string. This meant that: - If the length was not correct, or the string passed in was not nul-terminated, the buffer would overflow. - If the string passed in contained embedded nul characters (e.g, in among the first length characters), any characters after the embedded nul would not be copied (but len would be updated to indicate they were). In practice, neither of the above would occur, since the length passed is always the known length of the string, usually as returned by strlen. However, to allow using this concat method to pass in strings that are not nul-terminated, its behaviour should change. This commit changes the method to use memcpy instead of strcpy, copying exactly the number of bytes passed in. For the current calls to this method, which pass a nul-terminated string, without embedded nul characters and a correct length, this behaviour should not change. However, this concat method can now also be used in the two cases mentioned above. Non-nul-terminated strings now work as expected and for strings with embedded newlines the entire string is copied as-is, instead of leaving uninitialized memory after the embedded nul byte. Note that a lot of operations will still only see the string up to the embedded nul byte, but that's not really fixable unless we reimplement functions like strcmp. --- hardware/arduino/avr/cores/arduino/WString.cpp | 6 +++--- hardware/arduino/sam/cores/arduino/WString.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/WString.cpp b/hardware/arduino/avr/cores/arduino/WString.cpp index 956df40337d..2b141c8192e 100644 --- a/hardware/arduino/avr/cores/arduino/WString.cpp +++ b/hardware/arduino/avr/cores/arduino/WString.cpp @@ -174,7 +174,7 @@ String & String::copy(const char *cstr, unsigned int length) return *this; } len = length; - strcpy(buffer, cstr); + memcpy(buffer, cstr, length); return *this; } @@ -194,7 +194,7 @@ void String::move(String &rhs) { if (buffer) { if (capacity >= rhs.len) { - strcpy(buffer, rhs.buffer); + memcpy(buffer, rhs.buffer, rhs.len); len = rhs.len; rhs.len = 0; return; @@ -266,7 +266,7 @@ unsigned char String::concat(const char *cstr, unsigned int length) if (!cstr) return 0; if (length == 0) return 1; if (!reserve(newlen)) return 0; - strcpy(buffer + len, cstr); + memcpy(buffer + len, cstr, length); len = newlen; return 1; } diff --git a/hardware/arduino/sam/cores/arduino/WString.cpp b/hardware/arduino/sam/cores/arduino/WString.cpp index 2311315ad49..dcfd508e4a6 100644 --- a/hardware/arduino/sam/cores/arduino/WString.cpp +++ b/hardware/arduino/sam/cores/arduino/WString.cpp @@ -176,7 +176,7 @@ String & String::copy(const char *cstr, unsigned int length) return *this; } len = length; - strcpy(buffer, cstr); + memcpy(buffer, cstr, length); return *this; } @@ -196,7 +196,7 @@ void String::move(String &rhs) { if (buffer) { if (capacity >= rhs.len) { - strcpy(buffer, rhs.buffer); + memcpy(buffer, rhs.buffer, rhs.len); len = rhs.len; rhs.len = 0; return; @@ -268,7 +268,7 @@ unsigned char String::concat(const char *cstr, unsigned int length) if (!cstr) return 0; if (length == 0) return 1; if (!reserve(newlen)) return 0; - strcpy(buffer + len, cstr); + memcpy(buffer + len, cstr, length); len = newlen; return 1; } From bf438eb0b71476393a7aa6bff684de848b6785e9 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 11:11:14 +0100 Subject: [PATCH 3/9] Use memmove in String::remove Before, it used strncpy, but that has undefined behaviour when the source and destination strings overlap. memove is guaranteed to work correctly in this case. Note that memmove simply copies all bytes, whereas strncpy stopped at the first nul-byte. This means this commit also makes remove work for strings that contain embedded nul bytes. --- hardware/arduino/avr/cores/arduino/WString.cpp | 2 +- hardware/arduino/sam/cores/arduino/WString.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/WString.cpp b/hardware/arduino/avr/cores/arduino/WString.cpp index 2b141c8192e..83efaca9e26 100644 --- a/hardware/arduino/avr/cores/arduino/WString.cpp +++ b/hardware/arduino/avr/cores/arduino/WString.cpp @@ -696,7 +696,7 @@ void String::remove(unsigned int index, unsigned int count){ if (count > len - index) { count = len - index; } char *writeTo = buffer + index; len = len - count; - strncpy(writeTo, buffer + index + count,len - index); + memmove(writeTo, buffer + index + count, len - index); buffer[len] = 0; } diff --git a/hardware/arduino/sam/cores/arduino/WString.cpp b/hardware/arduino/sam/cores/arduino/WString.cpp index dcfd508e4a6..c3d6c6eb779 100644 --- a/hardware/arduino/sam/cores/arduino/WString.cpp +++ b/hardware/arduino/sam/cores/arduino/WString.cpp @@ -698,7 +698,7 @@ void String::remove(unsigned int index, unsigned int count){ if (count > len - index) { count = len - index; } char *writeTo = buffer + index; len = len - count; - strncpy(writeTo, buffer + index + count,len - index); + memmove(writeTo, buffer + index + count, len - index); buffer[len] = 0; } From 432c648e250a9a7c18dabbdf4ad455fde7004395 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 12:27:00 +0100 Subject: [PATCH 4/9] Simplify String::concat(char) Now concat(const char*, unsigned int) no longer requires a nul-terminated string, we can simplify the concat(char) method to just pass the address of the single character instead of having to create buffer with nul-termination. --- hardware/arduino/avr/cores/arduino/WString.cpp | 5 +---- hardware/arduino/sam/cores/arduino/WString.cpp | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/WString.cpp b/hardware/arduino/avr/cores/arduino/WString.cpp index 83efaca9e26..c7ecf2e15cf 100644 --- a/hardware/arduino/avr/cores/arduino/WString.cpp +++ b/hardware/arduino/avr/cores/arduino/WString.cpp @@ -279,10 +279,7 @@ unsigned char String::concat(const char *cstr) unsigned char String::concat(char c) { - char buf[2]; - buf[0] = c; - buf[1] = 0; - return concat(buf, 1); + return concat(&c, 1); } unsigned char String::concat(unsigned char num) diff --git a/hardware/arduino/sam/cores/arduino/WString.cpp b/hardware/arduino/sam/cores/arduino/WString.cpp index c3d6c6eb779..5de15806d5e 100644 --- a/hardware/arduino/sam/cores/arduino/WString.cpp +++ b/hardware/arduino/sam/cores/arduino/WString.cpp @@ -281,10 +281,7 @@ unsigned char String::concat(const char *cstr) unsigned char String::concat(char c) { - char buf[2]; - buf[0] = c; - buf[1] = 0; - return concat(buf, 1); + return concat(&c, 1); } unsigned char String::concat(unsigned char num) From e7d934d15ee449a33c396105d0ab732b2205036e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 09:25:17 +0100 Subject: [PATCH 5/9] Make String::concat(const char *, unsigned int) public This method is useful when receiving data from external sources that pass an explicit length instead of a NUL-terminated string. --- hardware/arduino/avr/cores/arduino/WString.h | 2 +- hardware/arduino/sam/cores/arduino/WString.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/WString.h b/hardware/arduino/avr/cores/arduino/WString.h index b047980877c..c4ea825b0b5 100644 --- a/hardware/arduino/avr/cores/arduino/WString.h +++ b/hardware/arduino/avr/cores/arduino/WString.h @@ -98,6 +98,7 @@ class String // concatenation is considered unsucessful. unsigned char concat(const String &str); unsigned char concat(const char *cstr); + unsigned char concat(const char *cstr, unsigned int length); unsigned char concat(char c); unsigned char concat(unsigned char c); unsigned char concat(int num); @@ -195,7 +196,6 @@ class String void init(void); void invalidate(void); unsigned char changeBuffer(unsigned int maxStrLen); - unsigned char concat(const char *cstr, unsigned int length); // copy and move String & copy(const char *cstr, unsigned int length); diff --git a/hardware/arduino/sam/cores/arduino/WString.h b/hardware/arduino/sam/cores/arduino/WString.h index b047980877c..c4ea825b0b5 100644 --- a/hardware/arduino/sam/cores/arduino/WString.h +++ b/hardware/arduino/sam/cores/arduino/WString.h @@ -98,6 +98,7 @@ class String // concatenation is considered unsucessful. unsigned char concat(const String &str); unsigned char concat(const char *cstr); + unsigned char concat(const char *cstr, unsigned int length); unsigned char concat(char c); unsigned char concat(unsigned char c); unsigned char concat(int num); @@ -195,7 +196,6 @@ class String void init(void); void invalidate(void); unsigned char changeBuffer(unsigned int maxStrLen); - unsigned char concat(const char *cstr, unsigned int length); // copy and move String & copy(const char *cstr, unsigned int length); From 555800b32dc980467e91512a010ed84a4cc5ef84 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 10:37:38 +0100 Subject: [PATCH 6/9] Add String(char *, unsigned) constructor This constructor allows converting a buffer containing a non-nul-terminated string to a String object, by explicitely passing the length. --- hardware/arduino/avr/cores/arduino/WString.cpp | 6 ++++++ hardware/arduino/avr/cores/arduino/WString.h | 1 + hardware/arduino/sam/cores/arduino/WString.cpp | 6 ++++++ hardware/arduino/sam/cores/arduino/WString.h | 1 + 4 files changed, 14 insertions(+) diff --git a/hardware/arduino/avr/cores/arduino/WString.cpp b/hardware/arduino/avr/cores/arduino/WString.cpp index c7ecf2e15cf..146757e56c9 100644 --- a/hardware/arduino/avr/cores/arduino/WString.cpp +++ b/hardware/arduino/avr/cores/arduino/WString.cpp @@ -31,6 +31,12 @@ String::String(const char *cstr) if (cstr) copy(cstr, strlen(cstr)); } +String::String(const char *cstr, unsigned int length) +{ + init(); + if (cstr) copy(cstr, length); +} + String::String(const String &value) { init(); diff --git a/hardware/arduino/avr/cores/arduino/WString.h b/hardware/arduino/avr/cores/arduino/WString.h index c4ea825b0b5..d8119afab39 100644 --- a/hardware/arduino/avr/cores/arduino/WString.h +++ b/hardware/arduino/avr/cores/arduino/WString.h @@ -57,6 +57,7 @@ class String // fails, the string will be marked as invalid (i.e. "if (s)" will // be false). String(const char *cstr = ""); + String(const char *cstr, unsigned int length); String(const String &str); String(const __FlashStringHelper *str); #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) diff --git a/hardware/arduino/sam/cores/arduino/WString.cpp b/hardware/arduino/sam/cores/arduino/WString.cpp index 5de15806d5e..00609e9642b 100644 --- a/hardware/arduino/sam/cores/arduino/WString.cpp +++ b/hardware/arduino/sam/cores/arduino/WString.cpp @@ -33,6 +33,12 @@ String::String(const char *cstr) if (cstr) copy(cstr, strlen(cstr)); } +String::String(const char *cstr, unsigned int length) +{ + init(); + if (cstr) copy(cstr, length); +} + String::String(const String &value) { init(); diff --git a/hardware/arduino/sam/cores/arduino/WString.h b/hardware/arduino/sam/cores/arduino/WString.h index c4ea825b0b5..d8119afab39 100644 --- a/hardware/arduino/sam/cores/arduino/WString.h +++ b/hardware/arduino/sam/cores/arduino/WString.h @@ -57,6 +57,7 @@ class String // fails, the string will be marked as invalid (i.e. "if (s)" will // be false). String(const char *cstr = ""); + String(const char *cstr, unsigned int length); String(const String &str); String(const __FlashStringHelper *str); #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) From 43b7fd1519e625eef1fb21a2c97377414750bf92 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 16 Mar 2014 12:01:53 +0100 Subject: [PATCH 7/9] Don't mess with the original in String::substring Before, substring would (temporarily) add a \0 in the original string at the end of the substring, so the substring could be copied into a new String object. However, now that the String::copy() method can work with non-nul-terminated strings (by passing an explicit length), this trick is not needed and we can just call the copy method instead. --- hardware/arduino/avr/cores/arduino/WString.cpp | 5 +---- hardware/arduino/sam/cores/arduino/WString.cpp | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/WString.cpp b/hardware/arduino/avr/cores/arduino/WString.cpp index 146757e56c9..4008645f87c 100644 --- a/hardware/arduino/avr/cores/arduino/WString.cpp +++ b/hardware/arduino/avr/cores/arduino/WString.cpp @@ -624,10 +624,7 @@ String String::substring(unsigned int left, unsigned int right) const String out; if (left >= len) return out; if (right > len) right = len; - char temp = buffer[right]; // save the replaced character - buffer[right] = '\0'; - out = buffer + left; // pointer arithmetic - buffer[right] = temp; //restore character + out.copy(buffer + left, right - left); return out; } diff --git a/hardware/arduino/sam/cores/arduino/WString.cpp b/hardware/arduino/sam/cores/arduino/WString.cpp index 00609e9642b..8bee712203c 100644 --- a/hardware/arduino/sam/cores/arduino/WString.cpp +++ b/hardware/arduino/sam/cores/arduino/WString.cpp @@ -626,10 +626,7 @@ String String::substring(unsigned int left, unsigned int right) const String out; if (left >= len) return out; if (right > len) right = len; - char temp = buffer[right]; // save the replaced character - buffer[right] = '\0'; - out = buffer + left; // pointer arithmetic - buffer[right] = temp; //restore character + out.copy(buffer + left, right - left); return out; } From deadaa299ce07780f73ff17601eafd170af145cb Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 17 Mar 2014 11:24:58 +0100 Subject: [PATCH 8/9] Add String::concat(const uint8_t *, unsigned int) version This just calls the char* version, but allows calling the method with a uint8_t* as well (which is not uncommon for buffers). --- hardware/arduino/avr/cores/arduino/WString.h | 1 + hardware/arduino/sam/cores/arduino/WString.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hardware/arduino/avr/cores/arduino/WString.h b/hardware/arduino/avr/cores/arduino/WString.h index d8119afab39..8c7443b5f81 100644 --- a/hardware/arduino/avr/cores/arduino/WString.h +++ b/hardware/arduino/avr/cores/arduino/WString.h @@ -100,6 +100,7 @@ class String unsigned char concat(const String &str); unsigned char concat(const char *cstr); unsigned char concat(const char *cstr, unsigned int length); + unsigned char concat(const uint8_t *cstr, unsigned int length) {return concat((const char*)cstr, length);} unsigned char concat(char c); unsigned char concat(unsigned char c); unsigned char concat(int num); diff --git a/hardware/arduino/sam/cores/arduino/WString.h b/hardware/arduino/sam/cores/arduino/WString.h index d8119afab39..8c7443b5f81 100644 --- a/hardware/arduino/sam/cores/arduino/WString.h +++ b/hardware/arduino/sam/cores/arduino/WString.h @@ -100,6 +100,7 @@ class String unsigned char concat(const String &str); unsigned char concat(const char *cstr); unsigned char concat(const char *cstr, unsigned int length); + unsigned char concat(const uint8_t *cstr, unsigned int length) {return concat((const char*)cstr, length);} unsigned char concat(char c); unsigned char concat(unsigned char c); unsigned char concat(int num); From f821d03110be81e0abf3204b11363463de8e3b94 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 4 Aug 2015 10:08:09 +0200 Subject: [PATCH 9/9] Add String(const uint8_t *, unsigned int) constructor This allows creating a String from a uint8_t[] or uint8_t* as well, without having to add explicit casts. --- hardware/arduino/avr/cores/arduino/WString.h | 1 + hardware/arduino/sam/cores/arduino/WString.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hardware/arduino/avr/cores/arduino/WString.h b/hardware/arduino/avr/cores/arduino/WString.h index 8c7443b5f81..a36f07363ed 100644 --- a/hardware/arduino/avr/cores/arduino/WString.h +++ b/hardware/arduino/avr/cores/arduino/WString.h @@ -58,6 +58,7 @@ class String // be false). String(const char *cstr = ""); String(const char *cstr, unsigned int length); + String(const uint8_t *cstr, unsigned int length) : String((const char*)cstr, length) {} String(const String &str); String(const __FlashStringHelper *str); #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) diff --git a/hardware/arduino/sam/cores/arduino/WString.h b/hardware/arduino/sam/cores/arduino/WString.h index 8c7443b5f81..a36f07363ed 100644 --- a/hardware/arduino/sam/cores/arduino/WString.h +++ b/hardware/arduino/sam/cores/arduino/WString.h @@ -58,6 +58,7 @@ class String // be false). String(const char *cstr = ""); String(const char *cstr, unsigned int length); + String(const uint8_t *cstr, unsigned int length) : String((const char*)cstr, length) {} String(const String &str); String(const __FlashStringHelper *str); #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)