FastLED 3.9.7
Loading...
Searching...
No Matches
str.h
1#pragma once
2
3#include <string.h>
4#include <stdint.h>
5
6#include "fl/ptr.h"
7#include "fl/template_magic.h"
8#include "fl/vector.h"
9#include "fl/namespace.h"
10
11#ifndef FASTLED_STR_INLINED_SIZE
12#define FASTLED_STR_INLINED_SIZE 64
13#endif
14
15namespace fl { // Mandatory namespace for this class since it has name collisions.
16
17template <size_t N> class StrN;
18
19// A copy on write string class. Fast to copy from another
20// Str object as read only pointers are shared. If the size
21// of the string is below FASTLED_STR_INLINED_SIZE then the
22// the entire string fits in the object and no heap allocation occurs.
23// When the string is large enough it will overflow into heap
24// allocated memory. Copy on write means that if the Str has
25// a heap buffer that is shared with another Str object, then
26// a copy is made and then modified in place.
27// If write() or append() is called then the internal data structure
28// will grow to accomodate the new data with extra space for future,
29// like a vector.
30class Str;
31
33// Implementation details.
34
35FASTLED_SMART_PTR(StringHolder);
36
38 public:
39 static void append(int val, StrN<64> *dst);
40 static bool isSpace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; }
41 static float parseFloat(const char *str, size_t len);
42 static bool isDigit(char c) { return c >= '0' && c <= '9'; }
43};
44
45class StringHolder : public fl::Referent {
46 public:
47 StringHolder(const char *str);
48 StringHolder(size_t length);
49 StringHolder(const char *str, size_t length);
51 bool isShared() const { return ref_count() > 1; }
52 void grow(size_t newLength);
53 bool hasCapacity(size_t newLength) const { return newLength <= mCapacity; }
54 const char *data() const { return mData; }
55 char *data() { return mData; }
56 size_t length() const { return mLength; }
57 size_t capacity() const { return mCapacity; }
58 bool copy(const char *str, size_t len) {
59 if ((len+1) > mCapacity) {
60 return false;
61 }
62 memcpy(mData, str, len);
63 mData[len] = '\0';
64 mLength = len;
65 return true;
66 }
67
68
69 private:
70 char *mData = nullptr;
71 size_t mLength = 0;
72 size_t mCapacity = 0;
73};
74
75template <size_t SIZE = 64> class StrN {
76 private:
77 size_t mLength = 0;
78 char mInlineData[SIZE] = {0};
79 StringHolderPtr mHeapData;
80
81 public:
82 // Constructors
83 StrN() = default;
84
85 // cppcheck-suppress-begin [operatorEqVarError]
86 template <size_t M> StrN(const StrN<M> &other) { copy(other); }
87 StrN(const char *str) {
88 size_t len = strlen(str);
89 mLength = len;
90 if (len + 1 <= SIZE) {
91 memcpy(mInlineData, str, len + 1);
92 mHeapData.reset();
93 } else {
94 mHeapData = StringHolderPtr::New(str);
95 }
96 }
97 StrN(const StrN &other) { copy(other); }
98 void copy(const char *str) {
99 size_t len = strlen(str);
100 mLength = len;
101 if (len + 1 <= SIZE) {
102 memcpy(mInlineData, str, len + 1);
103 mHeapData.reset();
104 } else {
105 if (mHeapData && !mHeapData->isShared()) {
106 // We are the sole owners of this data so we can modify it
107 mHeapData->copy(str, len);
108 return;
109 }
110 mHeapData.reset();
111 mHeapData = StringHolderPtr::New(str);
112 }
113 }
114 StrN &operator=(const StrN &other) {
115 copy(other);
116 return *this;
117 }
118 template <size_t M> StrN &operator=(const StrN<M> &other) {
119 copy(other);
120 return *this;
121 }
122 // cppcheck-suppress-end
123
124 bool operator==(const StrN &other) const {
125 return strcmp(c_str(), other.c_str()) == 0;
126 }
127
128 bool operator!=(const StrN &other) const {
129 return strcmp(c_str(), other.c_str()) != 0;
130 }
131
132 void copy(const char* str, size_t len) {
133 mLength = len;
134 if (len + 1 <= SIZE) {
135 memcpy(mInlineData, str, len + 1);
136 mHeapData.reset();
137 } else {
138 mHeapData = StringHolderPtr::New(str, len);
139 }
140 }
141
142 template <size_t M> void copy(const StrN<M> &other) {
143 size_t len = other.size();
144 if (len + 1 <= SIZE) {
145 memcpy(mInlineData, other.c_str(), len + 1);
146 mHeapData.reset();
147 } else {
148 if (other.mHeapData) {
149 mHeapData = other.mHeapData;
150 } else {
151 mHeapData = StringHolderPtr::New(other.c_str());
152 }
153 }
154 mLength = len;
155 }
156
157 size_t capacity() const {
158 return mHeapData ? mHeapData->capacity() : SIZE;
159 }
160
161 size_t write(int n) {
162 StrN<64> dst;
163 StringFormatter::append(n, &dst); // Inlined size should suffice
164 return write(dst.c_str(), dst.size());
165 }
166
167 size_t write(const uint8_t *data, size_t n) {
168 const char *str = reinterpret_cast<const char *>(data);
169 return write(str, n);
170 }
171
172 size_t write(const char *str, size_t n) {
173 size_t newLen = mLength + n;
174 if (mHeapData && !mHeapData->isShared()) {
175 if (!mHeapData->hasCapacity(newLen)) {
176 mHeapData->grow(newLen * 3 / 2); // Grow by 50%
177 }
178 memcpy(mHeapData->data() + mLength, str, n);
179 mLength = newLen;
180 mHeapData->data()[mLength] = '\0';
181 return mLength;
182 }
183 if (newLen + 1 <= SIZE) {
184 memcpy(mInlineData + mLength, str, n);
185 mLength = newLen;
186 mInlineData[mLength] = '\0';
187 return mLength;
188 }
189 mHeapData.reset();
190 StringHolderPtr newData = StringHolderPtr::New(newLen);
191 if (newData) {
192 memcpy(newData->data(), c_str(), mLength);
193 memcpy(newData->data() + mLength, str, n);
194 newData->data()[newLen] = '\0';
195 mHeapData = newData;
196 mLength = newLen;
197 }
198 mHeapData = newData;
199 return mLength;
200 }
201
202 size_t write(char c) { return write(&c, 1); }
203
204 size_t write(uint8_t c) {
205 const char *str = reinterpret_cast<const char *>(&c);
206 return write(str, 1);
207 }
208
209 // Destructor
210 ~StrN() {}
211
212 // Accessors
213 size_t size() const { return mLength; }
214 size_t length() const { return mLength; }
215 const char *c_str() const {
216 return mHeapData ? mHeapData->data() : mInlineData;
217 }
218
219 char *c_str_mutable() {
220 return mHeapData ? mHeapData->data() : mInlineData;
221 }
222
223 char &operator[](size_t index) {
224 if (index >= mLength) {
225 static char dummy = '\0';
226 return dummy;
227 }
228 return c_str_mutable()[index];
229 }
230
231 const char &operator[](size_t index) const {
232 if (index >= mLength) {
233 static char dummy = '\0';
234 return dummy;
235 }
236 return c_str()[index];
237 }
238
239 bool empty() const { return mLength == 0; }
240
241 // Append method
242
243
244 bool operator<(const StrN &other) const {
245 return strcmp(c_str(), other.c_str()) < 0;
246 }
247
248 template<size_t M> bool operator<(const StrN<M> &other) const {
249 return strcmp(c_str(), other.c_str()) < 0;
250 }
251
252 void reserve(size_t newCapacity) {
253 // If capacity is less than current length, do nothing
254 if (newCapacity <= mLength) {
255 return;
256 }
257
258 // If new capacity fits in inline buffer, no need to allocate
259 if (newCapacity + 1 <= SIZE) {
260 return;
261 }
262
263 // If we already have unshared heap data with sufficient capacity, do nothing
264 if (mHeapData && !mHeapData->isShared() && mHeapData->hasCapacity(newCapacity)) {
265 return;
266 }
267
268 // Need to allocate new storage
269 StringHolderPtr newData = StringHolderPtr::New(newCapacity);
270 if (newData) {
271 // Copy existing content
272 memcpy(newData->data(), c_str(), mLength);
273 newData->data()[mLength] = '\0';
274 mHeapData = newData;
275 }
276 }
277
278 void clear(bool freeMemory = false) {
279 mLength = 0;
280 if (freeMemory && mHeapData) {
281 mHeapData.reset();
282 }
283 }
284
285
286
287 int16_t find(const char &value) const {
288 for (size_t i = 0; i < mLength; ++i) {
289 if (c_str()[i] == value) {
290 return i;
291 }
292 }
293 return -1;
294 }
295
296 StrN substring(size_t start, size_t end) const {
297 if (start >= mLength) {
298 return StrN();
299 }
300 if (end > mLength) {
301 end = mLength;
302 }
303 if (start >= end) {
304 return StrN();
305 }
306 StrN out;
307 out.copy(c_str() + start, end - start);
308 return out;
309 }
310
311
312
313 StrN trim() const {
314 StrN out;
315 size_t start = 0;
316 size_t end = mLength;
317 while (start < mLength && StringFormatter::isSpace(c_str()[start])) {
318 start++;
319 }
320 while (end > start && StringFormatter::isSpace(c_str()[end - 1])) {
321 end--;
322 }
323 return substring(start, end);
324 }
325
326 float toFloat() const {
327 return StringFormatter::parseFloat(c_str(), mLength);
328 }
329
330
331
332 private:
333 StringHolderPtr mData;
334};
335
336class Str : public StrN<FASTLED_STR_INLINED_SIZE> {
337 public:
339 Str(const char *str) : StrN<FASTLED_STR_INLINED_SIZE>(str) {}
340 Str(const Str &other) : StrN<FASTLED_STR_INLINED_SIZE>(other) {}
341 template <size_t M>
342 Str(const StrN<M> &other) : StrN<FASTLED_STR_INLINED_SIZE>(other) {}
343 Str &operator=(const Str &other) {
344 copy(other);
345 return *this;
346 }
347
348 Str& append(const char *str) { write(str, strlen(str)); return *this; }
349 Str& append(const char *str, size_t len) { write(str, len); return *this; }
350 Str& append(char c) { write(&c, 1); return *this; }
351 Str& append(int n) { write(n); return *this; }
352 Str& append(const StrN &str) { write(str.c_str(), str.size()); return *this; }
353
354
355};
356
357
358
359} // namespace fl
Definition str.h:336
Definition str.h:75
Implements the FastLED namespace macros.
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16