AceCommon  1.6.1
Arduino library for low-level common functions and features with no external dependencies
KString.cpp
1 #include <Arduino.h>
2 #include <string.h>
3 #include "KString.h"
4 
5 namespace ace_common {
6 
7 int KString::compareTo(const char* s) {
8  if (string_ == s) { return 0; }
9  if (string_ == nullptr) { return -1; }
10  if (s == nullptr) { return 1; }
11 
12  const char* a = (const char*) string_;
13  const char* b = s;
14  while (true) {
15  uint8_t cb = *b;
16 
17  Restart:
18  // Extract the current character depending on the 'stringType_'. Normally
19  // doing a conditional check inside an inner loop is not good for
20  // performance. However, when I pulled this code out into a template
21  // function (using a thin-wrapper CString and FString), the
22  // AceTime/AutoBenchmark program showed that it made no perceptible
23  // difference in performance. I think this is because there is enough
24  // overhead in the rest of function to make this conditional code
25  // unimportant.
26  uint8_t ca = (stringType_ == kTypeCstring) ? *a : pgm_read_byte(a);
27 
28  if (ca != cb) {
29  // If ca is a keyword reference, then compare against the keyword.
30  // Recursive keyword substitution not allowed, because I don't want to
31  // make this function recursive.
32  if (0 < ca && ca < numKeywords_) {
33  KStringKeywords keywords(keywordType_, keywords_);
34  const char* k = keywords.get(ca);
35  while (true) {
36  ca = (keywordType_ == kTypeCstring) ? *k : pgm_read_byte(k);
37  cb = *b;
38  if (ca == '\0') {
39  a++;
40  // Terrible goto, but it's the easiest way to bail out of the inner
41  // loop and continue the outer loop at the correct character.
42  goto Restart;
43  }
44  if (ca != cb) {
45  return (int) ca - (int) cb;
46  }
47  k++;
48  b++;
49  }
50  } else {
51  return (int) ca - (int) cb;
52  }
53  }
54  if (ca == '\0') return 0;
55  a++;
56  b++;
57  }
58 }
59 
60 int KString::compareTo(const KString& s) {
61  if (this == &s) { return 0; }
62  if (string_ == s.string_) { return 0; }
63  if (string_ == nullptr) { return -1; }
64  if (s.string_ == nullptr) { return 1; }
65 
66  KStringIterator aiter(*this);
67  KStringIterator biter(s);
68  while (true) {
69  char ca = aiter.get();
70  char cb = biter.get();
71 
72  if (ca == cb) {
73  if (ca == '\0') {
74  return 0;
75  }
76  } else {
77  return ca - cb;
78  }
79  aiter.next();
80  biter.next();
81  }
82 }
83 
84 void KString::printTo(Print& printer) {
85  const char* s = (const char*) string_;
86  if (s == nullptr) return;
87 
88  while (true) {
89  // Same comment as above, doing a conditional check inside an inner loop is
90  // usually not good for performance. But the templatized version of
91  // compareTo() made no difference, and this function which outputs to a
92  // Printer is not expected to be in a performance critical section.
93  char c = (stringType_ == kTypeCstring) ? *s : pgm_read_byte(s);
94 
95  s++;
96  if (c == 0) break;
97  if (c < numKeywords_) {
98  if (keywordType_ == kTypeCstring) {
99  printer.print((const char*) keywords_[(uint8_t) c]);
100  } else {
101  printer.print((const __FlashStringHelper*)
102  pgm_read_ptr(keywords_ + (uint8_t) c));
103  }
104  } else {
105  printer.write(c);
106  }
107  }
108 }
109 
111  // We don't support recursive compression fragments (i.e. compress tokens
112  // within fragments) so this does NOT need to be a loop.
113  char c = getInternal(firstType_, firstPtr_);
114  if (c == '\0') {
115  if (secondPtr_ != nullptr) {
116  // pop the stack
117  firstPtr_ = secondPtr_;
118  firstType_ = secondType_;
119  secondPtr_ = nullptr;
120  secondType_ = KString::kTypeCstring; // not used, can be anything
121 
122  // advance one character
123  firstPtr_++;
124  c = getInternal(firstType_, firstPtr_);
125  }
126  }
127 
128  if (c != '\0' && c < 0x20) { // fragment keyword string
129  // push the stack
130  secondPtr_ = firstPtr_;
131  secondType_ = firstType_;
132 
133  KStringKeywords keywords(ks_.keywordType_, ks_.keywords_);
134  firstPtr_ = keywords.get((uint8_t) c);
135  firstType_ = ks_.keywordType_;
136  c = getInternal(firstType_, firstPtr_);
137  }
138 
139  return c;
140 }
141 
142 } // ace_common
An interator that points to a character inside a KString.
Definition: KString.h:198
void next()
Advance the iterator one character,.
Definition: KString.h:221
char get()
Return the current character referenced by the iterator.
Definition: KString.cpp:110
A thin helper object around an array of const char* in regular memory, or an array of const __FlashSt...
Definition: KString.h:242
const char * get(uint8_t i) const
Return the string pointer of index i.
Definition: KString.h:260
A wrapper class around a normal c-string or Arduino f-string which is encoded and compressed using ke...
Definition: KString.h:53
void printTo(Print &printer)
Expand and print the current string to the given printer.
Definition: KString.cpp:84
int compareTo(const char *s)
Compare this string against a c-string s and return <0, 0 or >0 if this string is <,...
Definition: KString.cpp:7