Skip to content

Commit e714f97

Browse files
committed
support String() class
1 parent a65f6b1 commit e714f97

File tree

6 files changed

+304
-15
lines changed

6 files changed

+304
-15
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313
- Mocks for `random` functions with seed control
1414
- Many original Arduino `#define`s
1515
- Mocks for pinMode, analog/digital read/write
16+
- Support for WString
1617

1718
### Changed
1819
- Made `wget` have quieter output
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include <ArduinoUnitTests.h>
2+
#include <Arduino.h>
3+
4+
unittest(string_constructors)
5+
{
6+
assertTrue(String(""));
7+
assertTrue(String(3));
8+
assertTrue(String(3L));
9+
assertTrue(String(String("hi")));
10+
assertTrue(String(3.4));
11+
}
12+
13+
unittest(string_assignment_and_misc)
14+
{
15+
String s;
16+
s = String("");
17+
assertEqual(0, s.length());
18+
s = String("hi");
19+
assertEqual(2, s.length());
20+
s += String(" there ");
21+
assertEqual("hi there ", s);
22+
s.concat(-10);
23+
assertEqual("hi there -10", s);
24+
s.reserve(40);
25+
assertEqual("hi there -10", s);
26+
assertTrue(String("FOO").equalsIgnoreCase(String("foo")));
27+
assertFalse(String("F00").equalsIgnoreCase(String("foo")));
28+
assertEqual('i', s[1]);
29+
assertEqual('i', s.charAt(1));
30+
s[1] = 'o';
31+
assertEqual("ho there -10", s);
32+
s.setCharAt(1, 'i');
33+
assertEqual("hi there -10", s);
34+
35+
s += " ";
36+
s += -3.141;
37+
assertEqual("hi there -10 -3.14", s);
38+
}
39+
40+
unittest(string_comparison)
41+
{
42+
assertEqual("-32768", String(-32768));
43+
assertEqual("65535", String(65535U));
44+
assertEqual("32767", String(32767));
45+
assertEqual("2147483647", String(2147483647L));
46+
assertEqual("-2147483648", String(-2147483648L));
47+
assertEqual("4294967295", String(4294967295UL));
48+
assertEqual("3.14", String(3.1415, 2));
49+
assertEqual("-3.14", String(-3.1415, 2));
50+
assertEqual("0.14", String(0.1415, 2));
51+
assertEqual("0.14", String(-0.1415, 2));
52+
53+
assertNotEqual(String("32767"), String(-32767));
54+
assertLess(String("a"), String("b"));
55+
56+
assertEqual(-32768, String(-32768).toInt());
57+
assertEqual(65535U, String(65535U).toInt());
58+
assertEqual(32767, String(32767).toInt());
59+
assertEqual(2147483647L, String(2147483647L).toInt());
60+
assertEqual(-2147483648L, String(-2147483648L).toInt());
61+
assertEqual(4294967295UL, String(4294967295UL).toInt());
62+
//assertEqual("3.141", String(3.1415));
63+
}
64+
65+
unittest(string_mods)
66+
{
67+
String s = " hey ";
68+
s.trim();
69+
assertEqual("hey", s);
70+
s.trim();
71+
assertEqual("hey", s);
72+
s = "";
73+
s.trim();
74+
assertEqual("", s);
75+
76+
// https://www.arduino.cc/en/Tutorial/StringRemove
77+
s = "Hello World!";
78+
s.remove(7);
79+
assertEqual("Hello W", s);
80+
s = "Hello World!";
81+
s.remove(2, 6);
82+
assertEqual("Herld!", s);
83+
}
84+
85+
unittest(string_find)
86+
{
87+
String s = "in for a penny, in for a pound";
88+
assertEqual(3, s.indexOf('f'));
89+
assertEqual(3, s.indexOf("for"));
90+
assertEqual(19, s.indexOf('f', 7));
91+
assertEqual(19, s.indexOf("for", 7));
92+
assertEqual(19, s.lastIndexOf('f'));
93+
assertEqual(19, s.lastIndexOf("for"));
94+
assertEqual("a penny", s.substr(7, 7));
95+
96+
assertTrue(s.startsWith("in for a penny"));
97+
assertTrue(s.startsWith("for a penny", 3));
98+
assertTrue(s.endsWith("in for a pound"));
99+
s.replace('i', 'o');
100+
assertEqual("on for a penny, on for a pound", s);
101+
s.replace("for a", "the");
102+
assertEqual("on the penny, on the pound", s);
103+
s.replace("p", "BRILLIANT");
104+
assertEqual("on the BRILLIANTenny, on the BRILLIANTound", s);
105+
s.replace("BRILLIANT", "p");
106+
assertEqual("on the penny, on the pound", s);
107+
s.replace("on the ", "");
108+
assertEqual("penny, pound", s);
109+
110+
// infinite loop test
111+
String e = "zuzu";
112+
e.replace("zu", "zuzu");
113+
assertEqual("zuzuzuzu", e);
114+
115+
// infinite loop test
116+
String i = "ii";
117+
i.replace('i', 'i');
118+
assertEqual("ii", i);
119+
}
120+
121+
122+
unittest_main()

cpp/arduino/Arduino.h

+7-14
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,21 @@ Mock Arduino.h library.
55
Where possible, variable names from the Arduino library are used to avoid conflicts
66
77
*/
8-
#ifndef __did_sized_types
9-
typedef unsigned char uint8_t;
10-
// typedef __uint16_t uint16_t;
11-
// typedef __uint32_t uint32_t;
12-
// typedef __uint64_t uint64_t;
13-
#define __did_sized_types
14-
#endif
15-
8+
// Chars and strings
9+
#include "WCharacter.h"
10+
#include "WString.h"
1611

12+
typedef bool boolean;
13+
typedef uint8_t byte;
1714

1815
#include "ArduinoDefines.h"
1916
#include "binary.h"
2017

2118
// Math and Trig
2219
#include "AvrMath.h"
2320

24-
typedef bool boolean;
25-
typedef uint8_t byte;
21+
22+
2623

2724

2825

@@ -83,10 +80,6 @@ GodmodeState* GODMODE();
8380
#define detachInterrupt(...) _NOP()
8481

8582

86-
87-
// Character stuff
88-
#include "WCharacter.h"
89-
9083
// TODO: correctly establish this per-board!
9184
#define F_CPU 1000000UL
9285
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )

cpp/arduino/AvrMath.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
//#include <math.h>
22
#pragma once
33

4-
#define abs(x) ((x)>0?(x):-(x))
54
#define constrain(x,l,h) ((x)<(l)?(l):((x)>(h)?(h):(x)))
65
#define map(x,inMin,inMax,outMin,outMax) (((x)-(inMin))*((outMax)-(outMin))/((inMax)-(inMin))+outMin)
6+
7+
#ifdef abs
8+
#undef abs
9+
#endif
10+
#define abs(x) ((x)>0?(x):-(x))
11+
12+
#ifdef max
13+
#undef max
14+
#endif
715
#define max(a,b) ((a)>(b)?(a):(b))
16+
17+
#ifdef min
18+
#undef min
19+
#endif
820
#define min(a,b) ((a)<(b)?(a):(b))
921
float pow(float, float);
1022
#define sq(x) ((x)*(x))

cpp/arduino/WString.h

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#pragma once
2+
3+
#include <stdlib.h>
4+
#include <string>
5+
#include <algorithm>
6+
#include <iostream>
7+
#include "AvrMath.h"
8+
9+
typedef std::string string;
10+
11+
12+
// Compatibility with string class
13+
class String: public string
14+
{
15+
public:
16+
17+
// allow "string s; if (s) {}"
18+
// http://www.artima.com/cppsource/safebool.html
19+
typedef void (String::*TTDNSCstring)() const;
20+
void ttdnsc() const {}
21+
operator TTDNSCstring() const { return &String::ttdnsc; }
22+
23+
private:
24+
static const char* digit(int val) {
25+
const char* bank = "0123456789ABCDEF";
26+
return bank + val;
27+
}
28+
29+
static string mytoa(unsigned long val, int base) {
30+
int n = val % base;
31+
string place = string(digit(n), 1);
32+
if (val < base) return place;
33+
return mytoa(val / base, base) + place;
34+
}
35+
36+
static string mytoas(long val, int base) {
37+
string ret = mytoa(abs(val), base);
38+
return 0 <= val ? ret : string("-") + ret;
39+
}
40+
41+
public:
42+
~String(void) {}
43+
String(const char *cstr = ""): string(cstr) {}
44+
String(const string &str): string(str.c_str()) {}
45+
String(const String &str): string(str.c_str()) {}
46+
explicit String(char c): string(&c, 1) {}
47+
48+
explicit String(unsigned char val, unsigned char base=10): string(mytoa(val, base)) {}
49+
explicit String(int val, unsigned char base=10): string(mytoas(val, base)) {}
50+
explicit String(unsigned int val , unsigned char base=10): string(mytoa(val, base)) {}
51+
explicit String(long val, unsigned char base=10): string(mytoas(val, base)) {}
52+
explicit String(unsigned long val, unsigned char base=10): string(mytoa(val, base)) {}
53+
54+
explicit String(float val, unsigned char decimalPlaces=2):
55+
string(mytoas(val, 10) + "." + mytoa(abs(val - (long)val) * pow(10, decimalPlaces), 10)) {}
56+
explicit String(double val, unsigned char decimalPlaces=2):
57+
string(mytoas(val, 10) + "." + mytoa(abs(val - (long)val) * pow(10, decimalPlaces), 10)) {}
58+
59+
String & operator = (const String &rhs) { assign(rhs); return *this; }
60+
String & operator = (const char *cstr) { assign(cstr); return *this; }
61+
62+
unsigned char reserve(unsigned int size) { return true; } // calling reserve(size) segfaults, no idea why
63+
64+
unsigned char concat(const String &str) { append(str); return 1; }
65+
unsigned char concat(const char *cstr) { append(cstr); return 1; }
66+
unsigned char concat(char c) { append((const char*)&c, 1); return 1; }
67+
unsigned char concat(unsigned char c) { append((const char*)&c, 1); return 1; }
68+
unsigned char concat(int num) { append(String(num)); return 1; }
69+
unsigned char concat(unsigned int num) { append(String(num)); return 1; }
70+
unsigned char concat(long num) { append(String(num)); return 1; }
71+
unsigned char concat(unsigned long num) { append(String(num)); return 1; }
72+
unsigned char concat(float num) { append(String(num)); return 1; }
73+
unsigned char concat(double num) { append(String(num)); return 1; }
74+
75+
String & operator += (const String &rhs) { concat(rhs); return *this; }
76+
String & operator += (const char *cstr) { concat(cstr); return *this; }
77+
String & operator += (char c) { concat(c); return *this; }
78+
String & operator += (unsigned char num) { concat(num); return *this; }
79+
String & operator += (int num) { concat(num); return *this; }
80+
String & operator += (unsigned int num) { concat(num); return *this; }
81+
String & operator += (long num) { concat(num); return *this; }
82+
String & operator += (unsigned long num) { concat(num); return *this; }
83+
String & operator += (float num) { concat(num); return *this; }
84+
String & operator += (double num) { concat(num); return *this; }
85+
86+
87+
int compareTo(const String &s) const { return compare(s); }
88+
unsigned char equals(const String &s) const { return compareTo(s) == 0; }
89+
unsigned char equals(const char *cstr) const { return compareTo(String(cstr)) == 0; }
90+
unsigned char equal(const String &s) const { return equals(s); }
91+
unsigned char equal(const char *cstr) const { return equals(cstr); }
92+
unsigned char equalsIgnoreCase(const String &s) const {
93+
String a = String(*this);
94+
String b = String(s);
95+
a.toUpperCase();
96+
b.toUpperCase();
97+
return a.compare(b) == 0;
98+
}
99+
100+
unsigned char startsWith(const String &prefix) const { return find(prefix) == 0; }
101+
unsigned char startsWith(const String &prefix, unsigned int offset) const { return find(prefix, offset) == offset; }
102+
unsigned char endsWith(const String &suffix) const { return rfind(suffix) == length() - suffix.length(); }
103+
104+
char charAt(unsigned int index) const { return operator[](index); }
105+
void setCharAt(unsigned int index, char c) { (*this)[index] = c; }
106+
107+
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const { copy((char*)buf, bufsize, index); }
108+
void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
109+
{ getBytes((unsigned char *)buf, bufsize, index); }
110+
111+
int indexOf( char ch ) const { return find(ch); }
112+
int indexOf( char ch, unsigned int fromIndex ) const { return find(ch, fromIndex); }
113+
int indexOf( const String &str ) const { return find(str); }
114+
int indexOf( const String &str, unsigned int fromIndex ) const { return find(str, fromIndex); }
115+
int lastIndexOf( char ch ) const { return rfind(ch); }
116+
int lastIndexOf( char ch, unsigned int fromIndex ) const { return rfind(ch, fromIndex); }
117+
int lastIndexOf( const String &str ) const { return rfind(str); }
118+
int lastIndexOf( const String &str, unsigned int fromIndex ) const { return rfind(str, fromIndex); }
119+
String substring( unsigned int beginIndex ) const { return String(substr(beginIndex)); }
120+
String substring( unsigned int beginIndex, unsigned int endIndex ) const { return String(substr(beginIndex, endIndex)); }
121+
122+
void replace(const String& target, const String& repl) {
123+
int i = 0;
124+
while ((i = find(target, i)) != npos) {
125+
assign(substr(0, i) + repl + substr(i + target.length()));
126+
i += repl.length();
127+
}
128+
}
129+
void replace(char target, char repl) {
130+
replace(String(target), String(repl));
131+
}
132+
void remove(unsigned int index) { assign(substr(0, index)); }
133+
void remove(unsigned int index, unsigned int count) { assign(substr(0, index) + substr(min(length(), index + count), count)); }
134+
void toLowerCase(void) { std::transform(begin(), end(), begin(), ::tolower); }
135+
void toUpperCase(void) { std::transform(begin(), end(), begin(), ::toupper); }
136+
137+
void trim(void) {
138+
int b;
139+
int e;
140+
for (b = 0; b < length() && isSpace(charAt(b)); ++b);
141+
for (e = length() - 1; e > b && isSpace(charAt(e)); --e);
142+
assign(substr(b, e - b + 1));
143+
}
144+
145+
long toInt(void) const { return std::stol(*this); }
146+
float toFloat(void) const { return std::stof(*this); }
147+
double toDouble(void) const { return std::stod(*this); }
148+
149+
};
150+
151+
template <typename T> inline std::ostream& operator << ( std::ostream& out, const std::basic_string<T>& bs ) {
152+
out << bs.c_str();
153+
return out;
154+
}
155+
156+
inline std::ostream& operator << ( std::ostream& out, const String& bs ) {
157+
out << bs.c_str();
158+
return out;
159+
}
160+

spec/cpp_library_spec.rb

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"TestSomething/test/good-godmode.cpp",
4040
"TestSomething/test/good-defines.cpp",
4141
"TestSomething/test/good-wcharacter.cpp",
42+
"TestSomething/test/good-wstring.cpp",
4243
"TestSomething/test/bad-null.cpp",
4344
]
4445
relative_paths = cpp_library.test_files.map { |f| f.split("SampleProjects/", 2)[1] }

0 commit comments

Comments
 (0)