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