Skip to content

Commit 8bd4dfb

Browse files
committed
Add project EfficientString
1 parent 857d059 commit 8bd4dfb

File tree

7 files changed

+1410
-1
lines changed

7 files changed

+1410
-1
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include "Polyfills.h" // adds move()
2+
#include "WString.h" // adds the instrumented String class
3+
4+
#define RESOURCE "outdoor_temp"
5+
#define API_KEY "0123456789"
6+
7+
// We must work in the namespace cpp4arduino to use the instrumented String
8+
namespace cpp4arduino {
9+
10+
// A function that takes a C-style string.
11+
// Calling this function doesn't makes a copy
12+
void passByPointer(const char *) {
13+
// (empty)
14+
}
15+
16+
// A function that takes a string by value.
17+
// Calling this function makes a copy of the String
18+
void passByValue(String) {
19+
// (empty)
20+
}
21+
22+
// A function that takes a string by referece.
23+
// Calling this function doesn't makes a copy
24+
void passByConstReference(const String &) {
25+
// (empty)
26+
}
27+
28+
void example1() {
29+
Serial.println("#1: Initialize from RAM");
30+
31+
String s = "example";
32+
}
33+
34+
void example2() {
35+
Serial.println("#2: Initialize from Flash");
36+
37+
String s = F("example");
38+
}
39+
40+
void example3() {
41+
Serial.println(F("#3: toCharArray()"));
42+
43+
String s = "example";
44+
char tmp[32];
45+
s.toCharArray(tmp, sizeof(tmp));
46+
passByPointer(tmp);
47+
}
48+
49+
void example4() {
50+
Serial.println(F("#4: c_str()"));
51+
52+
String s = "example";
53+
passByPointer(s.c_str());
54+
}
55+
56+
void example5() {
57+
Serial.println(F("#5: Pass by value"));
58+
59+
String s = "example";
60+
passByValue(s);
61+
}
62+
63+
void example6() {
64+
Serial.println(F("#6: Pass by const reference"));
65+
66+
String s = "example";
67+
passByConstReference(s);
68+
}
69+
70+
void example7() {
71+
Serial.println(F("#7: Move instance"));
72+
73+
String s = "example";
74+
passByValue(move(s));
75+
}
76+
77+
void example8() {
78+
Serial.println(F("#8: Append temporaries"));
79+
80+
// Construct "/api/outdoor_temp?key=0123456789"
81+
String path = String("/api/") + RESOURCE + "?key=" + API_KEY;
82+
83+
passByConstReference(path);
84+
}
85+
86+
void example9() {
87+
Serial.println(F("#9: Mutate instance"));
88+
89+
// Construct "/api/outdoor_temp?key=0123456789"
90+
String path("/api/");
91+
path += RESOURCE;
92+
path += "?key=";
93+
path += API_KEY;
94+
95+
passByConstReference(path);
96+
}
97+
98+
void example10() {
99+
Serial.println(F("#10: Call reserve"));
100+
101+
// Construct "/api/outdoor_temp?key=0123456789"
102+
String path;
103+
path.reserve(64);
104+
path += "/api/";
105+
path += RESOURCE;
106+
path += "?key=";
107+
path += API_KEY;
108+
109+
passByConstReference(path);
110+
}
111+
112+
void example11() {
113+
Serial.println(F("#11: Pass a null string to the constructor"));
114+
115+
// Construct "/api/outdoor_temp?key=0123456789"
116+
String path((char *)0);
117+
path.reserve(64);
118+
path += "/api/";
119+
path += RESOURCE;
120+
path += "?key=";
121+
path += API_KEY;
122+
123+
passByConstReference(path);
124+
}
125+
126+
void example12() {
127+
Serial.println(F("#12: Concat at compile time"));
128+
129+
// Construct "/api/outdoor_temp?key=0123456789"
130+
String path = "/api/" RESOURCE "?key=" API_KEY;
131+
132+
passByConstReference(path);
133+
}
134+
135+
} // namespace cpp4arduino
136+
137+
void setup() {
138+
Serial.begin(9600);
139+
while (!Serial)
140+
continue;
141+
142+
cpp4arduino::example1();
143+
Serial.println();
144+
cpp4arduino::example2();
145+
Serial.println();
146+
cpp4arduino::example3();
147+
Serial.println();
148+
cpp4arduino::example4();
149+
Serial.println();
150+
cpp4arduino::example5();
151+
Serial.println();
152+
cpp4arduino::example6();
153+
Serial.println();
154+
cpp4arduino::example7();
155+
Serial.println();
156+
cpp4arduino::example8();
157+
Serial.println();
158+
cpp4arduino::example9();
159+
Serial.println();
160+
cpp4arduino::example10();
161+
Serial.println();
162+
cpp4arduino::example11();
163+
Serial.println();
164+
cpp4arduino::example12();
165+
}
166+
167+
void loop() {}

EfficientString/LoggingProxies.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Wrappers on top of the standard functions to log what happens.
2+
3+
#include <Arduino.h>
4+
5+
namespace cpp4arduino {
6+
7+
template <typename... Args>
8+
inline void log(const __FlashStringHelper *fmt, Args... args) {
9+
char buffer[64];
10+
sprintf_P(buffer, (PGM_P)fmt, args...);
11+
Serial.println(buffer);
12+
}
13+
14+
inline void *malloc(size_t n) {
15+
void *result = ::malloc(n);
16+
log(F("- malloc(%u) -> %p"), n, result);
17+
return result;
18+
}
19+
20+
inline void *realloc(void *p, size_t n) {
21+
void *result = ::realloc(p, n);
22+
if (p == 0)
23+
log(F("- malloc(%u) -> %p"), n, result);
24+
else if (n == 0)
25+
log(F("- free(%p)"), p);
26+
else if (n && p)
27+
log(F("- realloc(%p, %u) -> %p"), p, n, result);
28+
return result;
29+
}
30+
31+
inline void free(void *p) {
32+
::free(p);
33+
log(F("- free(%p)"), p);
34+
}
35+
36+
inline void memcpy(char *dst, const char *src, size_t n) {
37+
::memcpy(dst, src, n);
38+
log(F("- memcpy(%p, %p, %u)"), dst, src, n);
39+
}
40+
41+
inline void memmove(char *dst, const char *src, size_t n) {
42+
::memmove(dst, src, n);
43+
log(F("- memmove(%p, %p, %u)"), dst, src, n);
44+
}
45+
46+
inline void strcpy(char *dst, const char *src) {
47+
::strcpy(dst, src);
48+
log(F("- strcpy(%p, %p) - %u bytes"), dst, src, ::strlen(src));
49+
}
50+
51+
inline void strncpy(char *dst, const char *src, size_t n) {
52+
::strncpy(dst, src, n);
53+
log(F("- strncpy(%p, %p, %u) - %u bytes"), dst, src, n,
54+
min(::strlen(src), n));
55+
}
56+
57+
inline void strcpy_P(char *dst, const char *src) {
58+
::strcpy_P(dst, src);
59+
log(F("- strcpy_P(%p, %p) - %u bytes"), dst, src, ::strlen_P(src));
60+
}
61+
} // namespace cpp4arduino

EfficientString/Polyfills.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Standard C++ functions missing in Arduino
2+
// cpp4arduino.com
3+
4+
#pragma once
5+
6+
namespace cpp4arduino {
7+
8+
template <class T>
9+
struct remove_reference {
10+
typedef T type;
11+
};
12+
template <class T>
13+
struct remove_reference<T &> {
14+
typedef T type;
15+
};
16+
template <class T>
17+
struct remove_reference<T &&> {
18+
typedef T type;
19+
};
20+
21+
template <typename T>
22+
constexpr typename remove_reference<T>::type &&move(T &&x) noexcept {
23+
return static_cast<typename remove_reference<T>::type &&>(x);
24+
}
25+
} // namespace cpp4arduino

EfficientString/README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
How to use the `String` class efficiently
2+
=========================================
3+
4+
This program demonstrate how you can improve the efficient of your code when using the `String` class:
5+
6+
* reduce copying
7+
* reduce allocations
8+
* reduce fragmentation
9+
10+
To show the effect of each calls, I modified the original `String` class from the [Arduino Core for AVR](https://github.com/arduino/ArduinoCore-avr/blob/2663be17272e19f00c55f3f2d8f1ebfac47158d6/cores/arduino/WString.h):
11+
12+
* I added calls to `Serial.println()`, so that we can see what happens in the Serial Monitor.
13+
* I removed the platform dependent features, so that we can run this program on other platforms.
14+
15+
The modified class is in a different namespace, to avoid the name clash.
16+
17+
Results
18+
-------
19+
20+
Here is the serial output of this program with the Arduino Core for AVR version 1.6.22:
21+
22+
```
23+
#1: Initialize from RAM
24+
- malloc(8) -> 0x23c
25+
- strcpy(0x23c, 0x133) - 7 bytes
26+
- free(0x23c)
27+
28+
#2: Initialize from Flash
29+
- malloc(8) -> 0x23c
30+
- strcpy_P(0x23c, 0xe3) - 7 bytes
31+
- free(0x23c)
32+
33+
#3: toCharArray()
34+
- malloc(8) -> 0x23c
35+
- strcpy(0x23c, 0x133) - 7 bytes
36+
- strncpy(0x8d6, 0x23c, 7) - 7 bytes
37+
- free(0x23c)
38+
39+
#4: c_str()
40+
- malloc(8) -> 0x23c
41+
- strcpy(0x23c, 0x133) - 7 bytes
42+
- free(0x23c)
43+
44+
#5: Pass by value
45+
- malloc(8) -> 0x23c
46+
- strcpy(0x23c, 0x133) - 7 bytes
47+
- malloc(8) -> 0x246
48+
- strcpy(0x246, 0x23c) - 7 bytes
49+
- free(0x246)
50+
- free(0x23c)
51+
52+
#6: Pass by const reference
53+
- malloc(8) -> 0x23c
54+
- strcpy(0x23c, 0x133) - 7 bytes
55+
- free(0x23c)
56+
57+
#7: Move instance
58+
- malloc(8) -> 0x23c
59+
- strcpy(0x23c, 0x133) - 7 bytes
60+
- free(0x23c)
61+
- free(0)
62+
63+
#8: Append temporaries
64+
- malloc(6) -> 0x23c
65+
- strcpy(0x23c, 0x155) - 5 bytes
66+
- malloc(6) -> 0x244
67+
- strcpy(0x244, 0x23c) - 5 bytes
68+
- realloc(0x244, 18) -> 0x244
69+
- strcpy(0x249, 0x15b) - 12 bytes
70+
- realloc(0x244, 23) -> 0x244
71+
- strcpy(0x255, 0x168) - 5 bytes
72+
- realloc(0x244, 33) -> 0x244
73+
- strcpy(0x25a, 0x184) - 10 bytes
74+
- malloc(33) -> 0x267
75+
- strcpy(0x267, 0x244) - 32 bytes
76+
- free(0x244)
77+
- free(0x23c)
78+
- free(0x267)
79+
80+
#9: Mutate instance
81+
- malloc(6) -> 0x23c
82+
- strcpy(0x23c, 0x155) - 5 bytes
83+
- realloc(0x23c, 18) -> 0x23c
84+
- strcpy(0x241, 0x15b) - 12 bytes
85+
- realloc(0x23c, 23) -> 0x23c
86+
- strcpy(0x24d, 0x168) - 5 bytes
87+
- realloc(0x23c, 33) -> 0x23c
88+
- strcpy(0x252, 0x184) - 10 bytes
89+
- free(0x23c)
90+
91+
#10: Call reserve
92+
- malloc(1) -> 0x23c
93+
- strcpy(0x23c, 0x11a) - 0 bytes
94+
- realloc(0x23c, 65) -> 0x23c
95+
- strcpy(0x23c, 0x155) - 5 bytes
96+
- strcpy(0x241, 0x15b) - 12 bytes
97+
- strcpy(0x24d, 0x168) - 5 bytes
98+
- strcpy(0x252, 0x184) - 10 bytes
99+
- free(0x23c)
100+
101+
#11: Pass a null string to the constructor
102+
- malloc(65) -> 0x23c
103+
- strcpy(0x23c, 0x155) - 5 bytes
104+
- strcpy(0x241, 0x15b) - 12 bytes
105+
- strcpy(0x24d, 0x168) - 5 bytes
106+
- strcpy(0x252, 0x184) - 10 bytes
107+
- free(0x23c)
108+
109+
#12: Concat at compile time
110+
- malloc(33) -> 0x23c
111+
- strcpy(0x23c, 0x16e) - 32 bytes
112+
- free(0x23c)
113+
```

0 commit comments

Comments
 (0)